init: repository upload
This commit is contained in:
commit
ab7d2651ce
|
@ -0,0 +1,9 @@
|
|||
.vscode
|
||||
.terraform
|
||||
*.terraform.*
|
||||
*.tfstate*
|
||||
.terraform.lock.hcl
|
||||
*symlink_provider.tf
|
||||
.DS_Store
|
||||
locals/
|
||||
**/private_keys
|
|
@ -0,0 +1,71 @@
|
|||
##-------------------------------------------------------------------------------
|
||||
## d4science - NETWORKING Folder
|
||||
##-------------------------------------------------------------------------------
|
||||
module "d4science-networking-folder" {
|
||||
source = "../assets/modules-fabric/v26/folder"
|
||||
parent = "organizations/${var.organization.id}"
|
||||
name = "Networking"
|
||||
folder_create = true
|
||||
|
||||
iam = {
|
||||
"roles/owner" = [
|
||||
module.d4science-networking-tfsa.iam_email,
|
||||
"group:foundationreply@d4science.org"
|
||||
]
|
||||
"roles/compute.xpnAdmin" = [module.d4science-networking-tfsa.iam_email] #to enable shared VPC
|
||||
#to enable 01-networking create the hub
|
||||
"roles/resourcemanager.projectCreator" = [module.d4science-networking-tfsa.iam_email]
|
||||
}
|
||||
|
||||
# iam_additive = {
|
||||
## "roles/resourcemanager.projectCreator" = [module.common-terraform-sa.iam_email] # required to create project within this folder
|
||||
# "roles/resourcemanager.projectCreator" = [] # required to create project within this folder
|
||||
#
|
||||
# }
|
||||
|
||||
}
|
||||
|
||||
|
||||
##-------------------------------------------------------------------------------
|
||||
## 01 - Networking - TF SA, impersonated to apply Terraform config
|
||||
##-------------------------------------------------------------------------------
|
||||
module "d4science-networking-tfsa" {
|
||||
source = "../assets/modules-fabric/v26/iam-service-account"
|
||||
|
||||
project_id = module.d4science-seed-project.project_id
|
||||
name = "d4science-com-tfnet-sa"
|
||||
prefix = var.prefix
|
||||
|
||||
iam = {
|
||||
"roles/iam.serviceAccountTokenCreator" = ["group:foundationreply@d4science.org"]
|
||||
#Impersonate service accounts (create OAuth2 access tokens, sign blobs or JWTs, etc).
|
||||
}
|
||||
|
||||
iam_billing_roles = {
|
||||
"${var.billing_account_id}" = ["roles/billing.user"]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
##-------------------------------------------------------------------------------
|
||||
## 01 - Networking - TF Bucket, store Terraform state
|
||||
##-------------------------------------------------------------------------------
|
||||
module "d4science-networking-tfbucket" {
|
||||
source = "../assets/modules-fabric/v26/gcs"
|
||||
|
||||
name = "d4science-com-ew8-foundation-tfnet-bkt"
|
||||
project_id = module.d4science-seed-project.project_id
|
||||
prefix = var.prefix
|
||||
|
||||
versioning = true
|
||||
iam = {
|
||||
"roles/storage.objectAdmin" = [module.d4science-networking-tfsa.iam_email]
|
||||
}
|
||||
|
||||
location = "EUROPE-WEST8"
|
||||
storage_class = "STANDARD"
|
||||
|
||||
labels = var.labels
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
##-------------------------------------------------------------------------------
|
||||
## 02 - Security - TF SA, impersonated to apply Terraform config
|
||||
##-------------------------------------------------------------------------------
|
||||
module "d4science-security-tfsa" {
|
||||
source = "../assets/modules-fabric/v26/iam-service-account"
|
||||
|
||||
project_id = module.d4science-seed-project.project_id
|
||||
name = "d4science-com-tfsec-sa"
|
||||
prefix = var.prefix
|
||||
|
||||
iam = {
|
||||
"roles/iam.serviceAccountTokenCreator" = ["group:foundationreply@d4science.org"]
|
||||
#Impersonate service accounts (create OAuth2 access tokens, sign blobs or JWTs, etc).
|
||||
}
|
||||
|
||||
iam_organization_roles = {
|
||||
"${var.organization.id}" = ["roles/resourcemanager.organizationAdmin"]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
##-------------------------------------------------------------------------------
|
||||
## 01 - Networking - TF Bucket, store Terraform state
|
||||
##-------------------------------------------------------------------------------
|
||||
module "d4science-security-tfbucket" {
|
||||
source = "../assets/modules-fabric/v26/gcs"
|
||||
|
||||
name = "d4science-com-ew8-foundation-tfsec-bkt"
|
||||
project_id = module.d4science-seed-project.project_id
|
||||
prefix = var.prefix
|
||||
|
||||
versioning = true
|
||||
iam = {
|
||||
"roles/storage.objectAdmin" = [module.d4science-security-tfsa.iam_email]
|
||||
}
|
||||
|
||||
location = "EUROPE-WEST8"
|
||||
storage_class = "STANDARD"
|
||||
|
||||
labels = var.labels
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
##-------------------------------------------------------------------------------
|
||||
## d4science/Prod Folder
|
||||
##-------------------------------------------------------------------------------
|
||||
module "d4science-prod-folder" {
|
||||
source = "../assets/modules-fabric/v26/folder"
|
||||
parent = "organizations/${var.organization.id}"
|
||||
name = "Prod"
|
||||
folder_create = true
|
||||
|
||||
#https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/tree/master/blueprints/factories/project-factory
|
||||
iam = {
|
||||
#Required for Project Factory
|
||||
"roles/logging.admin" = [module.d4science-project-factory-prod-tfsa.iam_email]
|
||||
"roles/owner" = [module.d4science-project-factory-prod-tfsa.iam_email]
|
||||
"roles/resourcemanager.folderAdmin" = [module.d4science-project-factory-prod-tfsa.iam_email]
|
||||
"roles/resourcemanager.projectCreator" = [module.d4science-project-factory-prod-tfsa.iam_email]
|
||||
|
||||
#To enable Shared VPC Service projects
|
||||
"roles/compute.xpnAdmin" = [module.d4science-networking-tfsa.iam_email]
|
||||
}
|
||||
}
|
||||
|
||||
##-------------------------------------------------------------------------------
|
||||
## d4science/Test Folder
|
||||
##-------------------------------------------------------------------------------
|
||||
module "d4science-test-folder" {
|
||||
source = "../assets/modules-fabric/v26/folder"
|
||||
parent = "organizations/${var.organization.id}"
|
||||
name = "Test"
|
||||
folder_create = true
|
||||
|
||||
#https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/tree/master/blueprints/factories/project-factory
|
||||
iam = {
|
||||
#Required for Project Factory
|
||||
"roles/logging.admin" = [module.d4science-project-factory-test-tfsa.iam_email]
|
||||
"roles/owner" = [module.d4science-project-factory-test-tfsa.iam_email]
|
||||
"roles/resourcemanager.folderAdmin" = [module.d4science-project-factory-test-tfsa.iam_email]
|
||||
"roles/resourcemanager.projectCreator" = [module.d4science-project-factory-test-tfsa.iam_email]
|
||||
|
||||
#To enable Shared VPC Service projects
|
||||
"roles/compute.xpnAdmin" = [module.d4science-networking-tfsa.iam_email]
|
||||
}
|
||||
}
|
||||
|
||||
##-------------------------------------------------------------------------------
|
||||
## 03 - Project Factory - TF SA, impersonated to apply Terraform config
|
||||
##-------------------------------------------------------------------------------
|
||||
|
||||
module "d4science-project-factory-test-tfsa" {
|
||||
source = "../assets/modules-fabric/v26/iam-service-account"
|
||||
|
||||
project_id = module.d4science-seed-project.project_id
|
||||
name = "d4science-test-tfprj-sa"
|
||||
prefix = var.prefix
|
||||
|
||||
iam = {
|
||||
"roles/iam.serviceAccountTokenCreator" = ["group:foundationreply@d4science.org"]
|
||||
#Impersonate service accounts (create OAuth2 access tokens, sign blobs or JWTs, etc).
|
||||
}
|
||||
|
||||
iam_billing_roles = {
|
||||
"${var.billing_account_id}" = ["roles/billing.admin"]
|
||||
}
|
||||
}
|
||||
|
||||
module "d4science-project-factory-prod-tfsa" {
|
||||
source = "../assets/modules-fabric/v26/iam-service-account"
|
||||
|
||||
project_id = module.d4science-seed-project.project_id
|
||||
name = "d4science-prod-tfprj-sa"
|
||||
prefix = var.prefix
|
||||
|
||||
iam = {
|
||||
"roles/iam.serviceAccountTokenCreator" = ["group:foundationreply@d4science.org"]
|
||||
#Impersonate service accounts (create OAuth2 access tokens, sign blobs or JWTs, etc).
|
||||
}
|
||||
|
||||
iam_billing_roles = {
|
||||
"${var.billing_account_id}" = ["roles/billing.admin"]
|
||||
}
|
||||
}
|
||||
|
||||
##############
|
||||
##############
|
||||
##############
|
||||
|
||||
##-------------------------------------------------------------------------------
|
||||
## 03 - Project Factory - TF Bucket, store Terraform state
|
||||
##-------------------------------------------------------------------------------
|
||||
|
||||
module "d4science-project-factory-test-tfbucket" {
|
||||
source = "../assets/modules-fabric/v26/gcs"
|
||||
|
||||
name = "d4science-test-ew8-foundation-tfprj-bkt"
|
||||
project_id = module.d4science-seed-project.project_id
|
||||
prefix = var.prefix
|
||||
|
||||
versioning = true
|
||||
iam = {
|
||||
"roles/storage.objectAdmin" = [module.d4science-project-factory-test-tfsa.iam_email]
|
||||
}
|
||||
|
||||
location = "EUROPE-WEST8"
|
||||
storage_class = "STANDARD"
|
||||
|
||||
labels = var.labels
|
||||
}
|
||||
|
||||
|
||||
|
||||
module "d4science-project-factory-prod-tfbucket" {
|
||||
source = "../assets/modules-fabric/v26/gcs"
|
||||
|
||||
name = "d4science-prod-ew8-foundation-tfprj-bkt"
|
||||
project_id = module.d4science-seed-project.project_id
|
||||
prefix = var.prefix
|
||||
|
||||
versioning = true
|
||||
iam = {
|
||||
"roles/storage.objectAdmin" = [module.d4science-project-factory-prod-tfsa.iam_email]
|
||||
}
|
||||
|
||||
location = "EUROPE-WEST8"
|
||||
storage_class = "STANDARD"
|
||||
|
||||
labels = var.labels
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
module "common-organization-monitoring-project" {
|
||||
source = "../assets/modules-fabric/v26/project"
|
||||
name = "d4science-com-monitoring-prj"
|
||||
project_create = true
|
||||
parent = "organizations/${var.organization.id}"
|
||||
billing_account = var.billing_account_id
|
||||
services = [
|
||||
"cloudresourcemanager.googleapis.com", #required for SA impersonification
|
||||
"iam.googleapis.com", #required for IAM and SA impersonification
|
||||
"secretmanager.googleapis.com", #required for secrets
|
||||
"monitoring.googleapis.com" #required for monitoring
|
||||
]
|
||||
|
||||
iam = {
|
||||
"roles/owner" = [
|
||||
module.d4science-organization-tfsa.iam_email,
|
||||
]
|
||||
"roles/resourcemanager.projectIamAdmin" = [
|
||||
module.d4science-project-factory-prod-tfsa.iam_email,
|
||||
module.d4science-project-factory-test-tfsa.iam_email
|
||||
],
|
||||
"roles/monitoring.notificationChannelViewer" = [
|
||||
module.d4science-project-factory-test-tfsa.iam_email,
|
||||
module.d4science-project-factory-prod-tfsa.iam_email
|
||||
]
|
||||
}
|
||||
|
||||
group_iam = {
|
||||
"foundationreply@d4science.org" = [
|
||||
"roles/owner"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
module "common-test-organization-monitoring-bucket" {
|
||||
source = "../assets/modules-fabric/v26/logging-bucket"
|
||||
parent_type = "project"
|
||||
parent = module.common-organization-monitoring-project.project_id
|
||||
id = "d4science-test-monitoring-sink-bucket"
|
||||
location = "europe-west8"
|
||||
description = "Terraform-managed."
|
||||
}
|
||||
|
||||
module "common-prod-organization-monitoring-bucket" {
|
||||
source = "../assets/modules-fabric/v26/logging-bucket"
|
||||
parent_type = "project"
|
||||
parent = module.common-organization-monitoring-project.project_id
|
||||
id = "d4science-prod-monitoring-sink-bucket"
|
||||
location = "europe-west4"
|
||||
description = "Terraform-managed."
|
||||
}
|
||||
|
||||
module "common-organization-monitoring-sa" {
|
||||
source = "../assets/modules-fabric/v26/iam-service-account"
|
||||
|
||||
project_id = module.common-organization-monitoring-project.project_id
|
||||
name = "d4science-com-monitoring-sa"
|
||||
prefix = var.prefix
|
||||
|
||||
iam_organization_roles = {
|
||||
"${var.organization.id}" = ["roles/monitoring.viewer"]
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_monitoring_notification_channel" "budget_alerting" {
|
||||
display_name = "Budget alerting"
|
||||
type = "email"
|
||||
project = module.common-organization-monitoring-project.project_id
|
||||
labels = {
|
||||
email_address = "budget@d4science.org"
|
||||
}
|
||||
user_labels = {}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
##-------------------------------------------------------------------------------
|
||||
## BCC_PAY - Organization
|
||||
##-------------------------------------------------------------------------------
|
||||
module "organization" {
|
||||
source = "../assets/modules-fabric/v26/organization"
|
||||
organization_id = "organizations/${var.organization.id}"
|
||||
|
||||
custom_roles = {
|
||||
# Allow gke policy creation. Assign this to gke sa
|
||||
"d4science_org_glb_gkemanagefwrules_role" = [
|
||||
"compute.networks.updatePolicy",
|
||||
"compute.firewalls.list",
|
||||
"compute.firewalls.get",
|
||||
"compute.firewalls.create",
|
||||
"compute.firewalls.update",
|
||||
"compute.firewalls.delete"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
# tfdoc:file:description Providers output files.
|
||||
|
||||
locals {
|
||||
providers = {
|
||||
"00-organization" = {
|
||||
bucket = module.d4science-organization-tfbucket.name
|
||||
service_account = module.d4science-organization-tfsa.email
|
||||
}
|
||||
"01-networking" = {
|
||||
bucket = module.d4science-networking-tfbucket.name
|
||||
service_account = module.d4science-networking-tfsa.email
|
||||
}
|
||||
"02-security" = {
|
||||
bucket = module.d4science-security-tfbucket.name
|
||||
service_account = module.d4science-security-tfsa.email
|
||||
}
|
||||
"03-project-factory-test" = {
|
||||
bucket = module.d4science-project-factory-test-tfbucket.name
|
||||
service_account = module.d4science-project-factory-test-tfsa.email
|
||||
}
|
||||
"03-project-factory-prod" = {
|
||||
bucket = module.d4science-project-factory-prod-tfbucket.name
|
||||
service_account = module.d4science-project-factory-prod-tfsa.email
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "local_file" "other_providers" {
|
||||
for_each = var.outputs_location == null ? {} : local.providers
|
||||
|
||||
file_permission = "0644"
|
||||
filename = "${path.module}/${var.outputs_location}/providers/${each.key}.providers.tf"
|
||||
content = templatefile("${path.module}/../assets/providers.tpl", {
|
||||
bucket = each.value.bucket
|
||||
sa = each.value.service_account
|
||||
prefix = try(each.value.prefix, null)
|
||||
})
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
# tfdoc:file:description Terraform tfvars output files.
|
||||
|
||||
locals {
|
||||
tfvars = {
|
||||
|
||||
billing_account_id = var.billing_account_id
|
||||
organization = var.organization
|
||||
prefix = var.prefix
|
||||
labels = var.labels
|
||||
groups = var.groups
|
||||
|
||||
# Global variables
|
||||
|
||||
seed-project = {
|
||||
project_id = module.d4science-seed-project.project_id
|
||||
project_number = module.d4science-seed-project.number
|
||||
}
|
||||
|
||||
service_accounts = {
|
||||
networking = module.d4science-networking-tfsa.iam_email
|
||||
security = module.d4science-security-tfsa.iam_email
|
||||
project_factory_test = module.d4science-project-factory-test-tfsa.iam_email
|
||||
project_factory_prod = module.d4science-project-factory-prod-tfsa.iam_email
|
||||
}
|
||||
|
||||
monitoring = {
|
||||
bucket-sink-test = module.common-test-organization-monitoring-bucket
|
||||
bucket-sink-prod = module.common-prod-organization-monitoring-bucket
|
||||
project_id = module.common-organization-monitoring-project.project_id
|
||||
channels = {
|
||||
"budget-alerting" = google_monitoring_notification_channel.budget_alerting.id
|
||||
}
|
||||
}
|
||||
|
||||
# Folders
|
||||
folders = {
|
||||
networking = {
|
||||
id = module.d4science-networking-folder.id
|
||||
name = module.d4science-networking-folder.name
|
||||
}
|
||||
prod = {
|
||||
id = module.d4science-prod-folder.id
|
||||
name = module.d4science-prod-folder.name
|
||||
}
|
||||
test = {
|
||||
id = module.d4science-test-folder.id
|
||||
name = module.d4science-test-folder.name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "local_file" "tfvars" {
|
||||
for_each = var.outputs_location == null ? {} : { 1 = 1 }
|
||||
file_permission = "0644"
|
||||
filename = "${path.module}/${var.outputs_location}/tfvars/00-organization.auto.tfvars.json"
|
||||
content = jsonencode(local.tfvars)
|
||||
}
|
||||
|
||||
output "tfvars" {
|
||||
sensitive = true
|
||||
value = local.tfvars
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
terraform {
|
||||
backend "gcs" {
|
||||
bucket = "d4science-com-ew8-foundation-tforg-bkt"
|
||||
impersonate_service_account = "d4science-com-tforg-sa@d4science-com-automation-prj.iam.gserviceaccount.com"
|
||||
}
|
||||
}
|
||||
|
||||
provider "google" {
|
||||
impersonate_service_account = "d4science-com-tforg-sa@d4science-com-automation-prj.iam.gserviceaccount.com"
|
||||
}
|
||||
|
||||
provider "google-beta" {
|
||||
impersonate_service_account = "d4science-com-tforg-sa@d4science-com-automation-prj.iam.gserviceaccount.com"
|
||||
}
|
||||
|
||||
provider "google" {
|
||||
alias = "no-impersonate"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#D4Science
|
||||
billing_account_id = "018258-BCA804-E9D4C6"
|
||||
|
||||
organization = {
|
||||
domain = "d4sscience.org"
|
||||
id = 392184451762
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
|
||||
module "d4science-seed-project" {
|
||||
source = "../assets/modules-fabric/v26/project"
|
||||
name = "d4science-com-automation-prj"
|
||||
project_create = true
|
||||
billing_account = var.billing_account_id
|
||||
parent = "organizations/${var.organization.id}"
|
||||
services = [
|
||||
"cloudresourcemanager.googleapis.com", #required for SA impersonification
|
||||
"serviceusage.googleapis.com", #required for SA impersonification
|
||||
"iam.googleapis.com", #required for IAM and SA impersonification
|
||||
"cloudbilling.googleapis.com", #required for project creation
|
||||
"accesscontextmanager.googleapis.com", #required for VPC SC
|
||||
"logging.googleapis.com", #required for sink creation
|
||||
"servicenetworking.googleapis.com", #required for Private Service Access
|
||||
"pubsub.googleapis.com", #required for PubSub
|
||||
"monitoring.googleapis.com", #required for Monitoring
|
||||
"billingbudgets.googleapis.com", #required for Billing budgets
|
||||
"orgpolicy.googleapis.com" #required for Organizational policies
|
||||
]
|
||||
|
||||
iam = {
|
||||
"roles/owner" = [
|
||||
module.d4science-organization-tfsa.iam_email,
|
||||
]
|
||||
}
|
||||
|
||||
group_iam = {
|
||||
"foundationreply@d4science.org" = [
|
||||
"roles/owner"
|
||||
]
|
||||
}
|
||||
|
||||
labels = var.labels
|
||||
}
|
||||
##-------------------------------------------------------------------------------
|
||||
## Terraform bucket for storing TFSTATE
|
||||
##-------------------------------------------------------------------------------
|
||||
module "d4science-organization-tfbucket" {
|
||||
source = "../assets/modules-fabric/v26/gcs"
|
||||
|
||||
name = "d4science-com-ew8-foundation-tforg-bkt"
|
||||
project_id = module.d4science-seed-project.project_id
|
||||
|
||||
prefix = var.prefix
|
||||
|
||||
versioning = true
|
||||
|
||||
iam = {
|
||||
"roles/storage.objectAdmin" = [module.d4science-organization-tfsa.iam_email]
|
||||
}
|
||||
|
||||
# Maintain last 3 versions (1 live plus 2).
|
||||
lifecycle_rules = {
|
||||
limit-versions = {
|
||||
action = {
|
||||
type = "Delete"
|
||||
}
|
||||
condition = {
|
||||
num_newer_versions = 3
|
||||
with_state = "ARCHIVED"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
location = "EUROPE-WEST8"
|
||||
storage_class = "STANDARD"
|
||||
|
||||
labels = var.labels
|
||||
}
|
||||
|
||||
|
||||
##-------------------------------------------------------------------------------
|
||||
## TF SA, impersonated to apply Terraform config
|
||||
##-------------------------------------------------------------------------------
|
||||
module "d4science-organization-tfsa" {
|
||||
source = "../assets/modules-fabric/v26/iam-service-account"
|
||||
|
||||
project_id = module.d4science-seed-project.project_id
|
||||
name = "d4science-com-tforg-sa"
|
||||
prefix = var.prefix
|
||||
|
||||
iam = {
|
||||
"roles/iam.serviceAccountTokenCreator" = ["group:foundationreply@d4science.org"]
|
||||
#Impersonate service accounts (create OAuth2 access tokens, sign blobs or JWTs, etc).
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
variable "billing_account_id" {
|
||||
description = "D4Science Billing account id."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "groups" {
|
||||
description = "Group names to grant organization-level permissions."
|
||||
type = map(string)
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "organization" {
|
||||
description = "Organization details."
|
||||
type = object({
|
||||
domain = string
|
||||
id = number
|
||||
})
|
||||
}
|
||||
|
||||
variable "prefix" {
|
||||
description = "Prefix for terraform resources"
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
variable "outputs_location" {
|
||||
description = "Assets path location relative to the module"
|
||||
type = string
|
||||
default = "../assets"
|
||||
}
|
||||
|
||||
variable "labels" {
|
||||
description = "Labels to add to the resources"
|
||||
type = map(string)
|
||||
default = {}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
name : "d4science-prod-ew4-vpc-con-sub"
|
||||
description : "Subnet assigned for D4Science prod environment's vpc connector"
|
||||
region : "europe-west4"
|
||||
ip_cidr_range : "10.254.2.0/28"
|
||||
|
||||
iam: # This section must be added after the creation of the service project to allow it to operate on the VPC
|
||||
roles/compute.networkUser:
|
||||
- serviceAccount:service-398661014261@gcp-sa-vpcaccess.iam.gserviceaccount.com
|
||||
- serviceAccount:398661014261@cloudservices.gserviceaccount.com
|
|
@ -0,0 +1,14 @@
|
|||
name : "d4science-prod-ew4-vre-sub"
|
||||
description : "Subnet assigned for D4Science prod environment's vre GKE cluster"
|
||||
region : "europe-west4"
|
||||
ip_cidr_range : "10.254.0.0/25"
|
||||
|
||||
secondary_ip_ranges: # map of secondary ip ranges
|
||||
pods: 10.250.0.0/17
|
||||
services: 10.250.128.0/17
|
||||
|
||||
iam: # This section must be added after the creation of the service project to allow it to operate on the VPC
|
||||
roles/compute.networkUser:
|
||||
- serviceAccount:service-783244529218@container-engine-robot.iam.gserviceaccount.com
|
||||
- serviceAccount:783244529218@cloudservices.gserviceaccount.com
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
name : "d4science-test-ew8-vpc-con-sub"
|
||||
description : "Subnet assigned for D4Science test environment's vpc connector"
|
||||
region : "europe-west8"
|
||||
ip_cidr_range : "10.254.66.0/28"
|
||||
|
||||
iam: # This section must be added after the creation of the service project to allow it to operate on the VPC
|
||||
roles/compute.networkUser:
|
||||
- serviceAccount:service-31481777243@gcp-sa-vpcaccess.iam.gserviceaccount.com
|
||||
- serviceAccount:31481777243@cloudservices.gserviceaccount.com
|
|
@ -0,0 +1,13 @@
|
|||
name : "d4science-test-ew8-vre-sub"
|
||||
description : "Subnet assigned for D4Science test environment's vre GKE cluster"
|
||||
region : "europe-west8"
|
||||
ip_cidr_range : "10.254.64.0/25"
|
||||
|
||||
secondary_ip_ranges: # map of secondary ip ranges
|
||||
pods: 10.251.0.0/17
|
||||
services: 10.251.128.0/17
|
||||
|
||||
iam: # This section must be added after the creation of the service project to allow it to operate on the VPC
|
||||
roles/compute.networkUser:
|
||||
- serviceAccount:service-804925782180@container-engine-robot.iam.gserviceaccount.com
|
||||
- serviceAccount:804925782180@cloudservices.gserviceaccount.com
|
|
@ -0,0 +1,35 @@
|
|||
locals {
|
||||
organization_vars = jsondecode(file("../assets/tfvars/00-organization.auto.tfvars.json"))
|
||||
}
|
||||
locals {
|
||||
|
||||
billing_account_id = local.organization_vars.billing_account_id
|
||||
organization = local.organization_vars.organization
|
||||
prefix = local.organization_vars.prefix
|
||||
labels = local.organization_vars.labels
|
||||
groups = local.organization_vars.groups
|
||||
|
||||
# Global variables
|
||||
|
||||
seed-project = {
|
||||
project_id = local.organization_vars.seed-project.project_id
|
||||
project_number = local.organization_vars.seed-project.project_number
|
||||
}
|
||||
|
||||
service_accounts = {
|
||||
networking = local.organization_vars.service_accounts.networking
|
||||
security = local.organization_vars.service_accounts.security
|
||||
project_factory_test = local.organization_vars.service_accounts.project_factory_test
|
||||
project_factory_prod = local.organization_vars.service_accounts.project_factory_prod
|
||||
|
||||
}
|
||||
|
||||
# Folders
|
||||
folders = {
|
||||
networking = {
|
||||
id = local.organization_vars.folders.networking.id
|
||||
name = local.organization_vars.folders.networking.name
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
# tfdoc:file:description Terraform tfvars output files.
|
||||
|
||||
locals {
|
||||
tfvars = {
|
||||
spoke-test = {
|
||||
project_id = module.d4science-networking-spoke-test-project.project_id
|
||||
number = module.d4science-networking-spoke-test-project.number
|
||||
network = module.d4science-networking-test-vpc.self_link
|
||||
subnets = {
|
||||
for k, v in module.d4science-networking-test-vpc.subnet_ips :
|
||||
k => {
|
||||
self_link = module.d4science-networking-test-vpc.subnets[k].self_link
|
||||
ip = v,
|
||||
secondary_ranges = module.d4science-networking-test-vpc.subnet_secondary_ranges[k]
|
||||
}
|
||||
}
|
||||
nat-address = module.d4science-networking-spoke-test-addresses.external_addresses["nat-ew8-00-addr-00"].address
|
||||
}
|
||||
|
||||
spoke-prod = {
|
||||
project_id = module.d4science-networking-spoke-prod-project.project_id
|
||||
number = module.d4science-networking-spoke-prod-project.number
|
||||
network = module.d4science-networking-prod-vpc.self_link
|
||||
subnets = {
|
||||
for k, v in module.d4science-networking-prod-vpc.subnet_ips :
|
||||
k => {
|
||||
self_link = module.d4science-networking-prod-vpc.subnets[k].self_link
|
||||
ip = v,
|
||||
secondary_ranges = module.d4science-networking-prod-vpc.subnet_secondary_ranges[k]
|
||||
}
|
||||
}
|
||||
nat-address = module.d4science-networking-spoke-prod-addresses.external_addresses["nat-ew4-00-addr-00"].address
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "local_file" "tfvars" {
|
||||
for_each = var.outputs_location == null ? {} : { 1 = 1 }
|
||||
file_permission = "0644"
|
||||
filename = "${path.module}/${var.outputs_location}/tfvars/01-networking.auto.tfvars.json"
|
||||
content = jsonencode(local.tfvars)
|
||||
}
|
||||
|
||||
|
||||
output "tfvars" {
|
||||
sensitive = true
|
||||
value = local.tfvars
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
terraform {
|
||||
backend "gcs" {
|
||||
bucket = "d4science-com-ew8-foundation-tfnet-bkt"
|
||||
impersonate_service_account = "d4science-com-tfnet-sa@d4science-com-automation-prj.iam.gserviceaccount.com"
|
||||
}
|
||||
|
||||
required_version = "~> 1.6.5"
|
||||
|
||||
required_providers {
|
||||
google = {
|
||||
source = "hashicorp/google"
|
||||
version = "4.84.0"
|
||||
}
|
||||
google-beta = {
|
||||
source = "hashicorp/google-beta"
|
||||
version = "4.84.0"
|
||||
}
|
||||
local = {
|
||||
source = "hashicorp/local"
|
||||
version = "~> 2.4.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "google" {
|
||||
impersonate_service_account = "d4science-com-tfnet-sa@d4science-com-automation-prj.iam.gserviceaccount.com"
|
||||
}
|
||||
|
||||
provider "google-beta" {
|
||||
impersonate_service_account = "d4science-com-tfnet-sa@d4science-com-automation-prj.iam.gserviceaccount.com"
|
||||
}
|
||||
|
||||
provider "google" {
|
||||
alias = "no-impersonate"
|
||||
}
|
||||
|
||||
provider "local" {}
|
|
@ -0,0 +1,117 @@
|
|||
#----------------------------------
|
||||
# Project
|
||||
#----------------------------------
|
||||
module "d4science-networking-spoke-prod-project" {
|
||||
source = "../assets/modules-fabric/v26/project"
|
||||
|
||||
name = "d4science-prod-spoke-prj"
|
||||
prefix = local.prefix
|
||||
billing_account = local.billing_account_id
|
||||
parent = local.folders.networking.id
|
||||
labels = local.labels
|
||||
|
||||
auto_create_network = false
|
||||
project_create = true
|
||||
|
||||
# Enable Shared VPC
|
||||
shared_vpc_host_config = {
|
||||
enabled = true
|
||||
# First run the next line must be commented, after you execute step 3 you can uncomment it
|
||||
# because you first need to create the service projects
|
||||
service_projects = var.service_projects["prod"]
|
||||
}
|
||||
|
||||
services = [
|
||||
"vpcaccess.googleapis.com", # to enable VPC Access Connector
|
||||
"container.googleapis.com",
|
||||
"certificatemanager.googleapis.com", # required for certificates
|
||||
"secretmanager.googleapis.com",
|
||||
"servicenetworking.googleapis.com"
|
||||
]
|
||||
|
||||
iam = {
|
||||
"roles/owner" = []
|
||||
"roles/editor" = ["serviceAccount:${module.d4science-networking-spoke-prod-project.service_accounts.cloud_services}"]
|
||||
"roles/compute.securityAdmin" = [local.service_accounts.security] #to create firewall rules
|
||||
|
||||
#https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/tree/master/blueprints/factories/project-factory
|
||||
#Required for Project Factory
|
||||
"roles/browser" = [local.service_accounts.project_factory_prod]
|
||||
"roles/compute.viewer" = [local.service_accounts.project_factory_prod]
|
||||
"roles/dns.admin" = [local.service_accounts.project_factory_prod]
|
||||
|
||||
# These must be done after creating the service project
|
||||
"roles/container.hostServiceAgentUser" = ["serviceAccount:service-783244529218@container-engine-robot.iam.gserviceaccount.com"]
|
||||
"organizations/${local.organization_vars.organization.id}/roles/d4science_org_glb_gkemanagefwrules_role" = ["serviceAccount:service-783244529218@container-engine-robot.iam.gserviceaccount.com"]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#----------------------------------
|
||||
# VPC prod with subnets
|
||||
#----------------------------------
|
||||
module "d4science-networking-prod-vpc" {
|
||||
source = "../assets/modules-fabric/v26/net-vpc"
|
||||
project_id = module.d4science-networking-spoke-prod-project.project_id
|
||||
name = "d4science-prod-vpc"
|
||||
factories_config = {
|
||||
subnets_folder = "${path.root}/data/subnets/prod"
|
||||
}
|
||||
psa_config = {
|
||||
ranges = { filestore = var.psa_ranges["prod"]["filestore"] }
|
||||
}
|
||||
}
|
||||
|
||||
#----------------------------------
|
||||
# Network Adresses (GLB and NAT)
|
||||
#----------------------------------
|
||||
|
||||
module "d4science-networking-spoke-prod-addresses" {
|
||||
source = "../assets/modules-fabric/v26/net-address"
|
||||
project_id = module.d4science-networking-spoke-prod-project.project_id
|
||||
external_addresses = {
|
||||
nat-ew4-00-addr-00 = { region = "europe-west4" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#----------------------------------
|
||||
# Cloud NAT (Spoke PROD)
|
||||
#----------------------------------
|
||||
|
||||
module "d4science-networking-spoke-prod-cloudnat-ew4" {
|
||||
source = "../assets/modules-fabric/v26/net-cloudnat"
|
||||
project_id = module.d4science-networking-spoke-prod-project.project_id
|
||||
region = "europe-west4"
|
||||
name = "d4science-prod-ew4-01-nat"
|
||||
addresses = [module.d4science-networking-spoke-prod-addresses.external_addresses["nat-ew4-00-addr-00"].self_link]
|
||||
|
||||
router_network = module.d4science-networking-prod-vpc.self_link
|
||||
config_source_subnets = "LIST_OF_SUBNETWORKS"
|
||||
subnetworks = [
|
||||
{
|
||||
self_link = module.d4science-networking-prod-vpc.subnet_self_links["europe-west4/d4science-prod-ew4-vre-sub"]
|
||||
config_source_ranges = ["ALL_IP_RANGES"]
|
||||
secondary_ranges = null
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
##----------------------------------
|
||||
## Private Google Access (PROD)
|
||||
##----------------------------------
|
||||
|
||||
module "d4science-networking-spoke-prod-pga" {
|
||||
source = "../assets/modules-custom/pga"
|
||||
project_id = module.d4science-networking-spoke-prod-project.project_id
|
||||
name = "d4science-prod-pga"
|
||||
domains = {
|
||||
artifact = true
|
||||
}
|
||||
config = {
|
||||
private = true
|
||||
}
|
||||
networks = [
|
||||
module.d4science-networking-prod-vpc.self_link
|
||||
]
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
#----------------------------------
|
||||
# Project
|
||||
#----------------------------------
|
||||
module "d4science-networking-spoke-test-project" {
|
||||
source = "../assets/modules-fabric/v26/project"
|
||||
|
||||
name = "d4science-test-spoke-prj"
|
||||
prefix = local.prefix
|
||||
billing_account = local.billing_account_id
|
||||
parent = local.folders.networking.id
|
||||
labels = local.labels
|
||||
|
||||
auto_create_network = false
|
||||
project_create = true
|
||||
|
||||
# Enable Shared VPC
|
||||
shared_vpc_host_config = {
|
||||
enabled = true
|
||||
# First run the next line must be commented, after you execute step 3 you can uncomment it
|
||||
# because you first need to create the service projects
|
||||
service_projects = var.service_projects["test"]
|
||||
}
|
||||
|
||||
services = [
|
||||
"vpcaccess.googleapis.com", # to enable VPC Access Connector
|
||||
"container.googleapis.com",
|
||||
"certificatemanager.googleapis.com", # required for certificates
|
||||
"secretmanager.googleapis.com",
|
||||
"servicenetworking.googleapis.com"
|
||||
]
|
||||
|
||||
iam = {
|
||||
"roles/owner" = []
|
||||
"roles/editor" = ["serviceAccount:${module.d4science-networking-spoke-test-project.service_accounts.cloud_services}"]
|
||||
"roles/compute.securityAdmin" = [local.service_accounts.security] #to create firewall rules
|
||||
|
||||
#https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/tree/master/blueprints/factories/project-factory
|
||||
#Required for Project Factory
|
||||
"roles/browser" = [local.service_accounts.project_factory_test]
|
||||
"roles/compute.viewer" = [local.service_accounts.project_factory_test]
|
||||
"roles/dns.admin" = [local.service_accounts.project_factory_test]
|
||||
|
||||
# These must be done after creating the service project
|
||||
"roles/container.hostServiceAgentUser" = ["serviceAccount:service-804925782180@container-engine-robot.iam.gserviceaccount.com"]
|
||||
"organizations/${local.organization_vars.organization.id}/roles/d4science_org_glb_gkemanagefwrules_role" = ["serviceAccount:service-804925782180@container-engine-robot.iam.gserviceaccount.com"]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#----------------------------------
|
||||
# VPC test with subnets
|
||||
#----------------------------------
|
||||
module "d4science-networking-test-vpc" {
|
||||
source = "../assets/modules-fabric/v26/net-vpc"
|
||||
project_id = module.d4science-networking-spoke-test-project.project_id
|
||||
name = "d4science-test-vpc"
|
||||
factories_config = {
|
||||
subnets_folder = "${path.root}/data/subnets/test"
|
||||
}
|
||||
psa_config = {
|
||||
ranges = { filestore = var.psa_ranges["test"]["filestore"] }
|
||||
}
|
||||
}
|
||||
|
||||
#----------------------------------
|
||||
# Network Adresses (GLB and NAT)
|
||||
#----------------------------------
|
||||
|
||||
module "d4science-networking-spoke-test-addresses" {
|
||||
source = "../assets/modules-fabric/v26/net-address"
|
||||
project_id = module.d4science-networking-spoke-test-project.project_id
|
||||
external_addresses = {
|
||||
nat-ew8-00-addr-00 = { region = "europe-west8" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#----------------------------------
|
||||
# Cloud NAT (Spoke TEST)
|
||||
#----------------------------------
|
||||
|
||||
module "d4science-networking-spoke-test-cloudnat-ew8" {
|
||||
source = "../assets/modules-fabric/v26/net-cloudnat"
|
||||
project_id = module.d4science-networking-spoke-test-project.project_id
|
||||
region = "europe-west8"
|
||||
name = "d4science-test-ew8-01-nat"
|
||||
addresses = [module.d4science-networking-spoke-test-addresses.external_addresses["nat-ew8-00-addr-00"].self_link]
|
||||
|
||||
router_network = module.d4science-networking-test-vpc.self_link
|
||||
config_source_subnets = "LIST_OF_SUBNETWORKS"
|
||||
subnetworks = [
|
||||
{
|
||||
self_link = module.d4science-networking-test-vpc.subnet_self_links["europe-west8/d4science-test-ew8-vre-sub"]
|
||||
config_source_ranges = ["ALL_IP_RANGES"]
|
||||
secondary_ranges = null
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
##----------------------------------
|
||||
## Private Google Access (TEST)
|
||||
##----------------------------------
|
||||
|
||||
module "d4science-networking-spoke-test-pga" {
|
||||
source = "../assets/modules-custom/pga"
|
||||
project_id = module.d4science-networking-spoke-test-project.project_id
|
||||
name = "d4science-test-pga"
|
||||
domains = {
|
||||
artifact = true
|
||||
}
|
||||
config = {
|
||||
private = true
|
||||
}
|
||||
networks = [
|
||||
module.d4science-networking-test-vpc.self_link
|
||||
]
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
service_projects = {
|
||||
networking : ["d4science-spoke-prod", "d4science-spoke-test"],
|
||||
test : ["d4science-test-vre-prj", "d4science-test-script-prj"],
|
||||
prod : ["d4science-prod-vre-prj", "d4science-prod-script-prj"]
|
||||
}
|
||||
|
||||
psa_ranges = {
|
||||
"test" : { "filestore" : "10.254.65.0/24" },
|
||||
"prod" : { "filestore" : "10.254.1.0/24" },
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
variable "outputs_location" {
|
||||
description = "Assets path location relative to the module"
|
||||
type = string
|
||||
default = "../assets"
|
||||
}
|
||||
|
||||
variable "service_projects" {
|
||||
description = "A map of lists reporting projects to attach to a shared VPC of each environment"
|
||||
type = map(list(string))
|
||||
default = {
|
||||
networking : [],
|
||||
test : [],
|
||||
prod : []
|
||||
}
|
||||
}
|
||||
|
||||
variable "psa_ranges" {
|
||||
description = "A map of maps of address ranges for private service access"
|
||||
type = map(map(string))
|
||||
default = {
|
||||
test : {}
|
||||
prod : {}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
health-check:
|
||||
- 35.191.0.0/16
|
||||
- 130.211.0.0/22
|
||||
- 108.170.220.0/23
|
||||
|
||||
vpc-nat-ranges:
|
||||
- 107.178.230.64/26
|
||||
- 35.199.224.0/19
|
||||
|
||||
google-restricted-api:
|
||||
- 199.36.153.4/30
|
||||
|
||||
google-private-api:
|
||||
- 199.36.153.8/30
|
||||
|
||||
#----------------------------------
|
||||
# VRE
|
||||
#----------------------------------
|
||||
# TEST
|
||||
d4science-test-vre-gke-controlplane:
|
||||
- 10.249.0.64/28
|
||||
d4science-test-vre-gke-pods:
|
||||
- 10.251.0.0/17
|
||||
d4science-test-vre-gke-services:
|
||||
- 10.251.128.0/17
|
||||
d4science-test-vre-gke-subnet:
|
||||
- 10.254.64.0/25
|
||||
|
||||
# PROD
|
||||
d4science-prod-vre-gke-controlplane:
|
||||
- 10.249.0.0/28
|
||||
d4science-prod-vre-gke-pods:
|
||||
- 10.250.0.0/17
|
||||
d4science-prod-vre-gke-services:
|
||||
- 10.250.128.0/17
|
||||
d4science-prod-vre-gke-subnet:
|
||||
- 10.254.0.0/25
|
|
@ -0,0 +1,7 @@
|
|||
# Cloud Functions ORG policies
|
||||
|
||||
cloudfunctions.allowedIngressSettings:
|
||||
rules:
|
||||
- allow:
|
||||
values:
|
||||
- ALLOW_INTERNAL_ONLY
|
|
@ -0,0 +1,18 @@
|
|||
# GCP ORG policies
|
||||
|
||||
gcp.detailedAuditLoggingMode:
|
||||
rules:
|
||||
- enforce: true
|
||||
|
||||
gcp.resourceLocations:
|
||||
rules:
|
||||
- allow:
|
||||
values:
|
||||
- in:eu-locations
|
||||
|
||||
gcp.restrictTLSVersion:
|
||||
rules:
|
||||
- deny:
|
||||
values:
|
||||
- TLS_VERSION_1
|
||||
- TLS_VERSION_1_1
|
|
@ -0,0 +1,15 @@
|
|||
# IAM ORG policies
|
||||
|
||||
iam.disableServiceAccountKeyCreation:
|
||||
rules:
|
||||
- enforce: true
|
||||
|
||||
iam.disableServiceAccountKeyUpload:
|
||||
rules:
|
||||
- enforce: true
|
||||
|
||||
iam.allowedPolicyMemberDomains:
|
||||
rules:
|
||||
- allow:
|
||||
values:
|
||||
- C01slynf8
|
|
@ -0,0 +1,25 @@
|
|||
# Cloud Run ORG policies
|
||||
|
||||
# Dopo svariati test, l'unica sintassi funzionante necessita di
|
||||
# due regole condizionali, con condizione opposta
|
||||
# Warning: al tempo del test (08/2023) la UI di Cloud Run risulta buggata
|
||||
# Testare Ingress tramite deploy gcloud
|
||||
run.allowedIngress:
|
||||
rules:
|
||||
- allow:
|
||||
values:
|
||||
- internal
|
||||
- internal-and-cloud-load-balancing
|
||||
condition:
|
||||
description: "Condizione per *non* permettere Ingress di tipo \"ALL\" ai servizi Cloud Run"
|
||||
expression: "!resource.matchTag(\"812186410362/cloudrun-allowIngress-tag\", \"allow-all\")"
|
||||
title: "Deny Ingress \"ALL\""
|
||||
- allow:
|
||||
values:
|
||||
- all
|
||||
- internal
|
||||
- internal-and-cloud-load-balancing
|
||||
condition:
|
||||
description: "Condizione per permettere Ingress di tipo \"ALL\" ai servizi Cloud Run"
|
||||
expression: "resource.matchTag(\"812186410362/cloudrun-allowIngress-tag\", \"allow-all\")"
|
||||
title: "Allow Ingress \"ALL\""
|
|
@ -0,0 +1,10 @@
|
|||
# Storage ORG policies
|
||||
|
||||
storage.publicAccessPrevention:
|
||||
rules:
|
||||
- enforce: true
|
||||
|
||||
storage.retentionPolicySeconds:
|
||||
rules:
|
||||
- allow:
|
||||
all: true
|
|
@ -0,0 +1,21 @@
|
|||
ingress:
|
||||
deny-ingress-all:
|
||||
deny: true
|
||||
description: "Block ingress."
|
||||
priority: 65000
|
||||
enable_logging:
|
||||
include_metadata: true
|
||||
rules:
|
||||
- protocol: all
|
||||
ports: []
|
||||
|
||||
egress:
|
||||
deny-egress-all:
|
||||
deny: true
|
||||
description: "Block egress."
|
||||
priority: 65001
|
||||
enable_logging:
|
||||
include_metadata: true
|
||||
rules:
|
||||
- protocol: all
|
||||
ports: []
|
|
@ -0,0 +1,103 @@
|
|||
ingress:
|
||||
d4science-prod-glb-vre-ig-alw-healthcheck-fwr:
|
||||
deny: false
|
||||
description: Allow Healthcheck.
|
||||
priority: 1000
|
||||
source_ranges:
|
||||
- health-check
|
||||
rules:
|
||||
- protocol: tcp
|
||||
ports:
|
||||
- 80
|
||||
- 443
|
||||
- 667
|
||||
- 8080
|
||||
d4science-prod-glb-ing-alw-gkecontrolplane-fwr:
|
||||
deny: false
|
||||
description: "Allow GKE master to reach VPC."
|
||||
priority: 1000
|
||||
source_ranges:
|
||||
- d4science-prod-vre-gke-controlplane
|
||||
rules:
|
||||
- protocol: all
|
||||
ports: []
|
||||
d4science-prod-glb-vre-ing-serverless-to-vpc-connector:
|
||||
deny: false
|
||||
description: Allow serverless services to reach vpc connectors.
|
||||
priority: 1000
|
||||
source_ranges:
|
||||
- vpc-nat-ranges
|
||||
rules:
|
||||
- protocol: tcp
|
||||
ports:
|
||||
- 667
|
||||
- protocol: udp
|
||||
ports:
|
||||
- 665
|
||||
- 666
|
||||
- protocol: icmp
|
||||
ports: []
|
||||
|
||||
egress:
|
||||
d4science-prod-glb-vre-egr-alw-dns-fwr:
|
||||
deny: false
|
||||
description: Allow DNS Egress.
|
||||
priority: 1000
|
||||
rules:
|
||||
- protocol: tcp
|
||||
ports: [53]
|
||||
- protocol: udp
|
||||
ports: [53]
|
||||
|
||||
d4science-prod-glb-vre-egr-alw-private-fwr:
|
||||
deny: false
|
||||
description: Allow PGA.
|
||||
priority: 1000
|
||||
destination_ranges:
|
||||
- google-private-api
|
||||
rules:
|
||||
- protocol: tcp
|
||||
ports: [443]
|
||||
|
||||
# This rule can be enforced only after the SA is created inside the service project. When you first create the cluster, activate it without the options for enforcing the SA.
|
||||
d4science-prod-glb-vre-egr-alw-cluster-fwr:
|
||||
deny: false
|
||||
description: "Allow cluster communication."
|
||||
use_service_accounts: true
|
||||
targets:
|
||||
- d4science-prod-vre-nodepoolsa@d4science-prod-vre-prj.iam.gserviceaccount.com
|
||||
destination_ranges:
|
||||
- 0.0.0.0/0
|
||||
priority: 1000
|
||||
rules:
|
||||
- protocol: all
|
||||
ports: []
|
||||
|
||||
d4science-prod-glb-vre-egr-vpc-connector-to-gke:
|
||||
deny: false
|
||||
description: "Allow vpc serverless connector to gke control plane communication."
|
||||
targets:
|
||||
- vpc-connector-europe-west4-d4science-prod-cf-vpc-con
|
||||
destination_ranges:
|
||||
- 0.0.0.0/0
|
||||
priority: 1000
|
||||
rules:
|
||||
- protocol: all
|
||||
ports: []
|
||||
|
||||
d4science-prod-glb-vre-egr-vpc-connector-to-serverless:
|
||||
deny: false
|
||||
description: Allow vpc connectors to reach serverless services.
|
||||
priority: 1000
|
||||
destination_ranges:
|
||||
- vpc-nat-ranges
|
||||
rules:
|
||||
- protocol: tcp
|
||||
ports:
|
||||
- 667
|
||||
- protocol: udp
|
||||
ports:
|
||||
- 665
|
||||
- 666
|
||||
- protocol: icmp
|
||||
ports: []
|
|
@ -0,0 +1,21 @@
|
|||
ingress:
|
||||
deny-ingress-all:
|
||||
deny: true
|
||||
description: "Block ingress."
|
||||
priority: 65000
|
||||
enable_logging:
|
||||
include_metadata: true
|
||||
rules:
|
||||
- protocol: all
|
||||
ports: []
|
||||
|
||||
egress:
|
||||
deny-egress-all:
|
||||
deny: true
|
||||
description: "Block egress."
|
||||
priority: 65001
|
||||
enable_logging:
|
||||
include_metadata: true
|
||||
rules:
|
||||
- protocol: all
|
||||
ports: []
|
|
@ -0,0 +1,103 @@
|
|||
ingress:
|
||||
d4science-test-glb-vre-ig-alw-healthcheck-fwr:
|
||||
deny: false
|
||||
description: Allow Healthcheck.
|
||||
priority: 1000
|
||||
source_ranges:
|
||||
- health-check
|
||||
rules:
|
||||
- protocol: tcp
|
||||
ports:
|
||||
- 80
|
||||
- 443
|
||||
- 667
|
||||
- 8080
|
||||
d4science-test-glb-ing-alw-gkecontrolplane-fwr:
|
||||
deny: false
|
||||
description: "Allow GKE master to reach VPC."
|
||||
priority: 1000
|
||||
source_ranges:
|
||||
- d4science-test-vre-gke-controlplane
|
||||
rules:
|
||||
- protocol: all
|
||||
ports: []
|
||||
d4science-test-glb-vre-ing-serverless-to-vpc-connector:
|
||||
deny: false
|
||||
description: Allow serverless services to reach vpc connectors.
|
||||
priority: 1000
|
||||
source_ranges:
|
||||
- vpc-nat-ranges
|
||||
rules:
|
||||
- protocol: tcp
|
||||
ports:
|
||||
- 667
|
||||
- protocol: udp
|
||||
ports:
|
||||
- 665
|
||||
- 666
|
||||
- protocol: icmp
|
||||
ports: []
|
||||
|
||||
egress:
|
||||
d4science-test-glb-vre-egr-alw-dns-fwr:
|
||||
deny: false
|
||||
description: Allow DNS Egress.
|
||||
priority: 1000
|
||||
rules:
|
||||
- protocol: tcp
|
||||
ports: [53]
|
||||
- protocol: udp
|
||||
ports: [53]
|
||||
|
||||
d4science-test-glb-vre-egr-alw-private-fwr:
|
||||
deny: false
|
||||
description: Allow PGA.
|
||||
priority: 1000
|
||||
destination_ranges:
|
||||
- google-private-api
|
||||
rules:
|
||||
- protocol: tcp
|
||||
ports: [443]
|
||||
|
||||
# This rule can be enforced only after the SA is created inside the service project. When you first create the cluster, activate it without the options for enforcing the SA.
|
||||
d4science-test-glb-vre-egr-alw-cluster-fwr:
|
||||
deny: false
|
||||
description: "Allow cluster communication."
|
||||
use_service_accounts: true
|
||||
targets:
|
||||
- d4science-test-vre-nodepoolsa@d4science-test-vre-prj.iam.gserviceaccount.com
|
||||
destination_ranges:
|
||||
- 0.0.0.0/0
|
||||
priority: 1000
|
||||
rules:
|
||||
- protocol: all
|
||||
ports: []
|
||||
|
||||
d4science-test-glb-vre-egr-vpc-connector-to-gke:
|
||||
deny: false
|
||||
description: "Allow vpc serverless connector to gke control plane communication."
|
||||
targets:
|
||||
- vpc-connector-europe-west8-d4science-test-cf-vpc-con
|
||||
destination_ranges:
|
||||
- 0.0.0.0/0
|
||||
priority: 1000
|
||||
rules:
|
||||
- protocol: all
|
||||
ports: []
|
||||
|
||||
d4science-test-glb-vre-egr-vpc-connector-to-serverless:
|
||||
deny: false
|
||||
description: Allow vpc connectors to reach serverless services.
|
||||
priority: 1000
|
||||
destination_ranges:
|
||||
- vpc-nat-ranges
|
||||
rules:
|
||||
- protocol: tcp
|
||||
ports:
|
||||
- 667
|
||||
- protocol: udp
|
||||
ports:
|
||||
- 665
|
||||
- 666
|
||||
- protocol: icmp
|
||||
ports: []
|
|
@ -0,0 +1,13 @@
|
|||
locals {
|
||||
networking_vars = jsondecode(file("../assets/tfvars/01-networking.auto.tfvars.json"))
|
||||
}
|
||||
locals {
|
||||
spoke-test = {
|
||||
project_id = local.networking_vars.spoke-test.project_id
|
||||
network = local.networking_vars.spoke-test.network
|
||||
}
|
||||
spoke-prod = {
|
||||
project_id = local.networking_vars.spoke-prod.project_id
|
||||
network = local.networking_vars.spoke-prod.network
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
##-------------------------------------------------------------------------------
|
||||
## 02-Security - Organization - ORG POLICIES
|
||||
##-------------------------------------------------------------------------------
|
||||
module "org" {
|
||||
source = "../assets/modules-fabric/v26/organization"
|
||||
organization_id = "organizations/${var.organization.id}"
|
||||
org_policies_data_path = "firewall/rules/org-policies"
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
terraform {
|
||||
backend "gcs" {
|
||||
bucket = "d4science-com-ew8-foundation-tfsec-bkt"
|
||||
impersonate_service_account = "d4science-com-tfsec-sa@d4science-com-automation-prj.iam.gserviceaccount.com"
|
||||
}
|
||||
|
||||
required_version = "~> 1.6.5"
|
||||
|
||||
required_providers {
|
||||
google = {
|
||||
source = "hashicorp/google"
|
||||
version = "4.84.0"
|
||||
}
|
||||
google-beta = {
|
||||
source = "hashicorp/google-beta"
|
||||
version = "4.84.0"
|
||||
}
|
||||
local = {
|
||||
source = "hashicorp/local"
|
||||
version = "~> 2.4.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "google" {
|
||||
impersonate_service_account = "d4science-com-tfsec-sa@d4science-com-automation-prj.iam.gserviceaccount.com"
|
||||
}
|
||||
|
||||
provider "google-beta" {
|
||||
impersonate_service_account = "d4science-com-tfsec-sa@d4science-com-automation-prj.iam.gserviceaccount.com"
|
||||
}
|
||||
|
||||
provider "google" {
|
||||
alias = "no-impersonate"
|
||||
}
|
||||
|
||||
provider "local" {}
|
|
@ -0,0 +1,17 @@
|
|||
#----------------------------------
|
||||
# Spoke PROD Firewall
|
||||
#----------------------------------
|
||||
module "d4science-security-spoke-prod-firewall" {
|
||||
source = "../assets/modules-fabric/v26/net-vpc-firewall"
|
||||
project_id = local.spoke-prod.project_id
|
||||
network = local.spoke-prod.network
|
||||
|
||||
default_rules_config = {
|
||||
disabled = true
|
||||
}
|
||||
|
||||
factories_config = {
|
||||
rules_folder = "firewall/rules/spoke-prod"
|
||||
cidr_tpl_file = "firewall/cidrs.yaml"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
#----------------------------------
|
||||
# Spoke TEST Firewall
|
||||
#----------------------------------
|
||||
module "d4science-security-spoke-test-firewall" {
|
||||
source = "../assets/modules-fabric/v26/net-vpc-firewall"
|
||||
project_id = local.spoke-test.project_id
|
||||
network = local.spoke-test.network
|
||||
|
||||
default_rules_config = {
|
||||
disabled = true
|
||||
}
|
||||
|
||||
factories_config = {
|
||||
rules_folder = "firewall/rules/spoke-test"
|
||||
cidr_tpl_file = "firewall/cidrs.yaml"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
organization = {
|
||||
domain = "d4sscience.org"
|
||||
id = 392184451762
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
variable "organization" {
|
||||
description = "Organization details."
|
||||
type = object({
|
||||
domain = string
|
||||
id = number
|
||||
})
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
locals {
|
||||
organization_vars = jsondecode(file("../assets/tfvars/00-organization.auto.tfvars.json"))
|
||||
networking_vars = jsondecode(file("../assets/tfvars/01-networking.auto.tfvars.json"))
|
||||
}
|
||||
locals {
|
||||
|
||||
billing_account_id = local.organization_vars.billing_account_id
|
||||
organization = local.organization_vars.organization
|
||||
prefix = local.organization_vars.prefix
|
||||
labels = local.organization_vars.labels
|
||||
groups = local.organization_vars.groups
|
||||
|
||||
# Global variables
|
||||
|
||||
seed-project = {
|
||||
project_id = local.organization_vars.seed-project.project_id
|
||||
project_number = local.organization_vars.seed-project.project_number
|
||||
}
|
||||
|
||||
service_accounts = {
|
||||
networking = local.organization_vars.service_accounts.networking
|
||||
security = local.organization_vars.service_accounts.security
|
||||
project_factory_prod = local.organization_vars.service_accounts.project_factory_prod
|
||||
}
|
||||
|
||||
networking = {
|
||||
spoke-prod-project = local.networking_vars.spoke-prod
|
||||
}
|
||||
|
||||
monitoring = {
|
||||
bucket-sink = local.organization_vars.monitoring.bucket-sink-prod
|
||||
project_id = local.organization_vars.monitoring.project_id
|
||||
channels = local.organization_vars.monitoring.channels
|
||||
}
|
||||
|
||||
# Folders
|
||||
folders = {
|
||||
prod = {
|
||||
id = local.organization_vars.folders.prod.id
|
||||
name = local.organization_vars.folders.prod.name
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
terraform {
|
||||
backend "gcs" {
|
||||
bucket = "d4science-prod-ew8-foundation-tfprj-bkt"
|
||||
impersonate_service_account = "d4science-prod-tfprj-sa@d4science-com-automation-prj.iam.gserviceaccount.com"
|
||||
}
|
||||
|
||||
required_version = "~> 1.6.5"
|
||||
|
||||
required_providers {
|
||||
google = {
|
||||
source = "hashicorp/google"
|
||||
version = "4.84.0"
|
||||
}
|
||||
google-beta = {
|
||||
source = "hashicorp/google-beta"
|
||||
version = "4.84.0"
|
||||
}
|
||||
local = {
|
||||
source = "hashicorp/local"
|
||||
version = "~> 2.4.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "google" {
|
||||
impersonate_service_account = "d4science-prod-tfprj-sa@d4science-com-automation-prj.iam.gserviceaccount.com"
|
||||
}
|
||||
|
||||
provider "google-beta" {
|
||||
impersonate_service_account = "d4science-prod-tfprj-sa@d4science-com-automation-prj.iam.gserviceaccount.com"
|
||||
}
|
||||
|
||||
provider "google" {
|
||||
alias = "no-impersonate"
|
||||
}
|
||||
|
||||
provider "local" {}
|
|
@ -0,0 +1,477 @@
|
|||
module "d4science-prod-budget-script-project" {
|
||||
source = "../assets/modules-fabric/v26/project"
|
||||
|
||||
name = "d4science-prod-script-prj"
|
||||
prefix = local.prefix
|
||||
billing_account = local.billing_account_id
|
||||
parent = local.folders.prod.id
|
||||
labels = local.labels
|
||||
|
||||
auto_create_network = false
|
||||
project_create = true
|
||||
|
||||
services = [
|
||||
"cloudresourcemanager.googleapis.com", #required for SA impersonification
|
||||
"iam.googleapis.com", #required for IAM and SA impersonification
|
||||
"cloudfunctions.googleapis.com", #required for Cloud Functions
|
||||
"pubsub.googleapis.com", #required for PubSub
|
||||
"billingbudgets.googleapis.com", #required for Billing alerting,
|
||||
"run.googleapis.com", #required for Cloud Functions 2nd gen
|
||||
"cloudbuild.googleapis.com", #required for Cloud Functions 2nd gen
|
||||
"eventarc.googleapis.com", #required for Cloud Functions 2nd gen
|
||||
"vpcaccess.googleapis.com", #required for Serverless VPC access
|
||||
]
|
||||
|
||||
iam = {
|
||||
"roles/owner" = [
|
||||
local.service_accounts.project_factory_prod,
|
||||
]
|
||||
}
|
||||
|
||||
group_iam = {
|
||||
"foundationreply@d4science.org" = [
|
||||
"roles/owner"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
module "d4science-prod-budget-alert-topic" {
|
||||
source = "../assets/modules-fabric/v26/pubsub"
|
||||
project_id = module.d4science-prod-budget-script-project.project_id
|
||||
name = "d4science-prod-budget-alerting-topic"
|
||||
}
|
||||
|
||||
module "d4science-billing-budget-overall-year-critical" {
|
||||
source = "../assets/modules-custom/billing-budget"
|
||||
billing_account = local.billing_account_id
|
||||
name = "Critical - Overall yearly budget"
|
||||
amount = 442500
|
||||
currency_code = "EUR"
|
||||
thresholds = {
|
||||
current = [0.95, 0.97]
|
||||
forecasted = []
|
||||
}
|
||||
calendar_period = "YEAR"
|
||||
resource_ancestors = ["organizations/${local.organization.id}"]
|
||||
notification_channels = [local.monitoring.channels["budget-alerting"]]
|
||||
}
|
||||
|
||||
module "d4science-billing-budget-overall-year" {
|
||||
source = "../assets/modules-custom/billing-budget"
|
||||
billing_account = local.billing_account_id
|
||||
name = "Overall yearly budget"
|
||||
amount = 442500
|
||||
currency_code = "EUR"
|
||||
thresholds = {
|
||||
current = [0.5, 0.8, 0.9]
|
||||
forecasted = []
|
||||
}
|
||||
calendar_period = "YEAR"
|
||||
resource_ancestors = ["organizations/${local.organization.id}"]
|
||||
notification_channels = [local.monitoring.channels["budget-alerting"]]
|
||||
}
|
||||
|
||||
module "d4science-billing-budget-overall-month" {
|
||||
source = "../assets/modules-custom/billing-budget"
|
||||
billing_account = local.billing_account_id
|
||||
name = "Overall monthly budget"
|
||||
amount = 36875
|
||||
currency_code = "EUR"
|
||||
thresholds = {
|
||||
current = [0.5, 0.8, 1.0]
|
||||
forecasted = []
|
||||
}
|
||||
calendar_period = "MONTH"
|
||||
resource_ancestors = ["organizations/${local.organization.id}"]
|
||||
notification_channels = [local.monitoring.channels["budget-alerting"]]
|
||||
}
|
||||
|
||||
module "d4science-prod-billing-budget-blue-cloud-total-year" {
|
||||
source = "../assets/modules-custom/billing-budget"
|
||||
billing_account = local.billing_account_id
|
||||
name = "Prod - GKE - BlueCloud cumulative yearly budget"
|
||||
amount = 168000
|
||||
currency_code = "EUR"
|
||||
thresholds = {
|
||||
current = [0.5, 0.8, 1.0]
|
||||
forecasted = []
|
||||
}
|
||||
labels = {
|
||||
"k8s-namespace" = "blue-cloud"
|
||||
}
|
||||
calendar_period = "YEAR"
|
||||
resource_ancestors = [local.folders.prod.id]
|
||||
notification_channels = [local.monitoring.channels["budget-alerting"]]
|
||||
pubsub_topic = module.d4science-prod-budget-alert-topic.id # Attention: this cannot be done without temporarily deactivating the org policy iam.allowedPolicyMemberDomains
|
||||
}
|
||||
|
||||
module "d4science-prod-billing-budget-i-marine-total-year" {
|
||||
source = "../assets/modules-custom/billing-budget"
|
||||
billing_account = local.billing_account_id
|
||||
name = "Prod - GKE - iMarine cumulative yearly budget"
|
||||
amount = 40500
|
||||
currency_code = "EUR"
|
||||
thresholds = {
|
||||
current = [0.5, 0.8, 1.0]
|
||||
forecasted = []
|
||||
}
|
||||
labels = {
|
||||
"k8s-namespace" = "i-marine"
|
||||
}
|
||||
calendar_period = "YEAR"
|
||||
resource_ancestors = [local.folders.prod.id]
|
||||
notification_channels = [local.monitoring.channels["budget-alerting"]]
|
||||
pubsub_topic = module.d4science-prod-budget-alert-topic.id # Attention: this cannot be done without temporarily deactivating the org policy iam.allowedPolicyMemberDomains
|
||||
}
|
||||
|
||||
module "d4science-prod-billing-budget-so-big-data-total-year" {
|
||||
source = "../assets/modules-custom/billing-budget"
|
||||
billing_account = local.billing_account_id
|
||||
name = "Prod - GKE - SoBigData cumulative yearly budget"
|
||||
amount = 147000
|
||||
currency_code = "EUR"
|
||||
thresholds = {
|
||||
current = [0.5, 0.8, 1.0]
|
||||
forecasted = []
|
||||
}
|
||||
labels = {
|
||||
"k8s-namespace" = "so-big-data"
|
||||
}
|
||||
calendar_period = "YEAR"
|
||||
resource_ancestors = [local.folders.prod.id]
|
||||
notification_channels = [local.monitoring.channels["budget-alerting"]]
|
||||
pubsub_topic = module.d4science-prod-budget-alert-topic.id # Attention: this cannot be done without temporarily deactivating the org policy iam.allowedPolicyMemberDomains
|
||||
}
|
||||
|
||||
module "d4science-prod-billing-budget-open-community-total-year" {
|
||||
source = "../assets/modules-custom/billing-budget"
|
||||
billing_account = local.billing_account_id
|
||||
name = "Prod - GKE - OpenCommunity cumulative yearly budget"
|
||||
amount = 87000
|
||||
currency_code = "EUR"
|
||||
thresholds = {
|
||||
current = [0.5, 0.8, 1.0]
|
||||
forecasted = []
|
||||
}
|
||||
labels = {
|
||||
"k8s-namespace" = "open-community"
|
||||
}
|
||||
calendar_period = "YEAR"
|
||||
resource_ancestors = [local.folders.prod.id]
|
||||
notification_channels = [local.monitoring.channels["budget-alerting"]]
|
||||
pubsub_topic = module.d4science-prod-budget-alert-topic.id # Attention: this cannot be done without temporarily deactivating the org policy iam.allowedPolicyMemberDomains
|
||||
}
|
||||
|
||||
module "d4science-prod-billing-budget-blue-cloud-year" {
|
||||
source = "../assets/modules-custom/billing-budget"
|
||||
billing_account = local.billing_account_id
|
||||
name = "Prod - GKE - BlueCloud namespace yearly budget"
|
||||
amount = 117000
|
||||
currency_code = "EUR"
|
||||
thresholds = {
|
||||
current = [0.5, 0.8, 1.0]
|
||||
forecasted = []
|
||||
}
|
||||
labels = {
|
||||
"k8s-label/d4science-namespace" = "blue-cloud"
|
||||
}
|
||||
calendar_period = "YEAR"
|
||||
resource_ancestors = [local.folders.prod.id]
|
||||
notification_channels = [local.monitoring.channels["budget-alerting"]]
|
||||
}
|
||||
|
||||
module "d4science-prod-billing-budget-i-marine-year" {
|
||||
source = "../assets/modules-custom/billing-budget"
|
||||
billing_account = local.billing_account_id
|
||||
name = "Prod - GKE - iMarine namespace yearly budget"
|
||||
amount = 22500
|
||||
currency_code = "EUR"
|
||||
thresholds = {
|
||||
current = [0.5, 0.8, 1.0]
|
||||
forecasted = []
|
||||
}
|
||||
labels = {
|
||||
"k8s-label/d4science-namespace" = "i-marine"
|
||||
}
|
||||
calendar_period = "YEAR"
|
||||
resource_ancestors = [local.folders.prod.id]
|
||||
notification_channels = [local.monitoring.channels["budget-alerting"]]
|
||||
}
|
||||
|
||||
module "d4science-prod-billing-budget-so-big-data-year" {
|
||||
source = "../assets/modules-custom/billing-budget"
|
||||
billing_account = local.billing_account_id
|
||||
name = "Prod - GKE - SoBigData namespace yearly budget"
|
||||
amount = 117000
|
||||
currency_code = "EUR"
|
||||
thresholds = {
|
||||
current = [0.5, 0.8, 1.0]
|
||||
forecasted = []
|
||||
}
|
||||
labels = {
|
||||
"k8s-label/d4science-namespace" = "so-big-data"
|
||||
}
|
||||
calendar_period = "YEAR"
|
||||
resource_ancestors = [local.folders.prod.id]
|
||||
notification_channels = [local.monitoring.channels["budget-alerting"]]
|
||||
}
|
||||
|
||||
module "d4science-prod-billing-budget-open-community-year" {
|
||||
source = "../assets/modules-custom/billing-budget"
|
||||
billing_account = local.billing_account_id
|
||||
name = "Prod - GKE - OpenCommunity namespace yearly budget"
|
||||
amount = 87000
|
||||
currency_code = "EUR"
|
||||
thresholds = {
|
||||
current = [0.5, 0.8, 1.0]
|
||||
forecasted = []
|
||||
}
|
||||
labels = {
|
||||
"k8s-label/d4science-namespace" = "open-community"
|
||||
}
|
||||
calendar_period = "YEAR"
|
||||
resource_ancestors = [local.folders.prod.id]
|
||||
notification_channels = [local.monitoring.channels["budget-alerting"]]
|
||||
}
|
||||
|
||||
module "d4science-prod-billing-budget-blue-cloud-datathon-year" {
|
||||
source = "../assets/modules-custom/billing-budget"
|
||||
billing_account = local.billing_account_id
|
||||
name = "Prod - GKE - BlueCloudDatathon namespace yearly budget"
|
||||
amount = 51000
|
||||
currency_code = "EUR"
|
||||
thresholds = {
|
||||
current = [0.5, 0.8, 1.0]
|
||||
forecasted = []
|
||||
}
|
||||
labels = {
|
||||
"k8s-label/d4science-namespace" = "blue-cloud-datathon"
|
||||
}
|
||||
calendar_period = "YEAR"
|
||||
resource_ancestors = [local.folders.prod.id]
|
||||
notification_channels = [local.monitoring.channels["budget-alerting"]]
|
||||
}
|
||||
|
||||
module "d4science-prod-billing-budget-i-marine-datathon-year" {
|
||||
source = "../assets/modules-custom/billing-budget"
|
||||
billing_account = local.billing_account_id
|
||||
name = "Prod - GKE - iMarineDatathon namespace yearly budget"
|
||||
amount = 18000
|
||||
currency_code = "EUR"
|
||||
thresholds = {
|
||||
current = [0.5, 0.8, 1.0]
|
||||
forecasted = []
|
||||
}
|
||||
labels = {
|
||||
"k8s-label/d4science-namespace" = "i-marine-datathon"
|
||||
}
|
||||
calendar_period = "YEAR"
|
||||
resource_ancestors = [local.folders.prod.id]
|
||||
notification_channels = [local.monitoring.channels["budget-alerting"]]
|
||||
}
|
||||
|
||||
module "d4science-prod-billing-budget-so-big-data-datathon-year" {
|
||||
source = "../assets/modules-custom/billing-budget"
|
||||
billing_account = local.billing_account_id
|
||||
name = "Prod - GKE - SoBigDataDatathon namespace yearly budget"
|
||||
amount = 30000
|
||||
currency_code = "EUR"
|
||||
thresholds = {
|
||||
current = [0.5, 0.8, 1.0]
|
||||
forecasted = []
|
||||
}
|
||||
labels = {
|
||||
"k8s-label/d4science-namespace" = "so-big-data-datathon"
|
||||
}
|
||||
calendar_period = "YEAR"
|
||||
resource_ancestors = [local.folders.prod.id]
|
||||
notification_channels = [local.monitoring.channels["budget-alerting"]]
|
||||
}
|
||||
|
||||
# module "d4science-prod-billing-budget-open-community-year" {
|
||||
# source = "../assets/modules-custom/billing-budget"
|
||||
# billing_account = local.billing_account_id
|
||||
# name = "Prod - GKE - OpenCommunityDatathon namespace yearly budget"
|
||||
# amount = 0
|
||||
# currency_code = "EUR"
|
||||
# thresholds = {
|
||||
# current = [0.5, 0.8, 1.0]
|
||||
# forecasted = []
|
||||
# }
|
||||
# labels = {
|
||||
# "k8s-label/d4science-namespace" = "open-community-datathon"
|
||||
# }
|
||||
# calendar_period = "YEAR"
|
||||
# resource_ancestors = [local.folders.prod.id]
|
||||
# notification_channels = [local.monitoring.channels["budget-alerting"]]
|
||||
# }
|
||||
|
||||
module "d4science-prod-billing-budget-blue-cloud-month" {
|
||||
source = "../assets/modules-custom/billing-budget"
|
||||
billing_account = local.billing_account_id
|
||||
name = "Prod - GKE - BlueCloud namespace monthly budget"
|
||||
amount = 9750
|
||||
currency_code = "EUR"
|
||||
thresholds = {
|
||||
current = [0.5, 0.8, 1.0]
|
||||
forecasted = []
|
||||
}
|
||||
labels = {
|
||||
"k8s-label/d4science-namespace" = "blue-cloud"
|
||||
}
|
||||
calendar_period = "MONTH"
|
||||
resource_ancestors = [local.folders.prod.id]
|
||||
notification_channels = [local.monitoring.channels["budget-alerting"]]
|
||||
}
|
||||
|
||||
module "d4science-prod-billing-budget-i-marine-month" {
|
||||
source = "../assets/modules-custom/billing-budget"
|
||||
billing_account = local.billing_account_id
|
||||
name = "Prod - GKE - iMarine namespace monthly budget"
|
||||
amount = 1875
|
||||
currency_code = "EUR"
|
||||
thresholds = {
|
||||
current = [0.5, 0.8, 1.0]
|
||||
forecasted = []
|
||||
}
|
||||
labels = {
|
||||
"k8s-label/d4science-namespace" = "i-marine"
|
||||
}
|
||||
calendar_period = "MONTH"
|
||||
resource_ancestors = [local.folders.prod.id]
|
||||
notification_channels = [local.monitoring.channels["budget-alerting"]]
|
||||
}
|
||||
|
||||
module "d4science-prod-billing-budget-so-big-data-month" {
|
||||
source = "../assets/modules-custom/billing-budget"
|
||||
billing_account = local.billing_account_id
|
||||
name = "Prod - GKE - SoBigData namespace monthly budget"
|
||||
amount = 9750
|
||||
currency_code = "EUR"
|
||||
thresholds = {
|
||||
current = [0.5, 0.8, 1.0]
|
||||
forecasted = []
|
||||
}
|
||||
labels = {
|
||||
"k8s-label/d4science-namespace" = "so-big-data"
|
||||
}
|
||||
calendar_period = "MONTH"
|
||||
resource_ancestors = [local.folders.prod.id]
|
||||
notification_channels = [local.monitoring.channels["budget-alerting"]]
|
||||
}
|
||||
|
||||
module "d4science-prod-billing-budget-open-community-month" {
|
||||
source = "../assets/modules-custom/billing-budget"
|
||||
billing_account = local.billing_account_id
|
||||
name = "Prod - GKE - OpenCommunity namespace monthly budget"
|
||||
amount = 7250
|
||||
currency_code = "EUR"
|
||||
thresholds = {
|
||||
current = [0.5, 0.8, 1.0]
|
||||
forecasted = []
|
||||
}
|
||||
labels = {
|
||||
"k8s-label/d4science-namespace" = "open-community"
|
||||
}
|
||||
calendar_period = "MONTH"
|
||||
resource_ancestors = [local.folders.prod.id]
|
||||
notification_channels = [local.monitoring.channels["budget-alerting"]]
|
||||
}
|
||||
|
||||
module "d4science-prod-billing-budget-blue-cloud-datathon-month" {
|
||||
source = "../assets/modules-custom/billing-budget"
|
||||
billing_account = local.billing_account_id
|
||||
name = "Prod - GKE - BlueCloudDatathon namespace monthly budget"
|
||||
amount = 4250
|
||||
currency_code = "EUR"
|
||||
thresholds = {
|
||||
current = [0.5, 0.8, 1.0]
|
||||
forecasted = []
|
||||
}
|
||||
labels = {
|
||||
"k8s-label/d4science-namespace" = "blue-cloud-datathon"
|
||||
}
|
||||
calendar_period = "MONTH"
|
||||
resource_ancestors = [local.folders.prod.id]
|
||||
notification_channels = [local.monitoring.channels["budget-alerting"]]
|
||||
}
|
||||
|
||||
module "d4science-prod-billing-budget-i-marine-datathon-month" {
|
||||
source = "../assets/modules-custom/billing-budget"
|
||||
billing_account = local.billing_account_id
|
||||
name = "Prod - GKE - iMarineDatathon namespace monthly budget"
|
||||
amount = 1500
|
||||
currency_code = "EUR"
|
||||
thresholds = {
|
||||
current = [0.5, 0.8, 1.0]
|
||||
forecasted = []
|
||||
}
|
||||
labels = {
|
||||
"k8s-label/d4science-namespace" = "i-marine-datathon"
|
||||
}
|
||||
calendar_period = "MONTH"
|
||||
resource_ancestors = [local.folders.prod.id]
|
||||
notification_channels = [local.monitoring.channels["budget-alerting"]]
|
||||
}
|
||||
|
||||
module "d4science-prod-billing-budget-so-big-data-datathon-month" {
|
||||
source = "../assets/modules-custom/billing-budget"
|
||||
billing_account = local.billing_account_id
|
||||
name = "Prod - GKE - SoBigDataDatathon namespace monthly budget"
|
||||
amount = 2500
|
||||
currency_code = "EUR"
|
||||
thresholds = {
|
||||
current = [0.5, 0.8, 1.0]
|
||||
forecasted = []
|
||||
}
|
||||
labels = {
|
||||
"k8s-label/d4science-namespace" = "so-big-data-datathon"
|
||||
}
|
||||
calendar_period = "MONTH"
|
||||
resource_ancestors = [local.folders.prod.id]
|
||||
notification_channels = [local.monitoring.channels["budget-alerting"]]
|
||||
}
|
||||
|
||||
# module "d4science-prod-billing-budget-open-community-datathon-month" {
|
||||
# source = "../assets/modules-custom/billing-budget"
|
||||
# billing_account = local.billing_account_id
|
||||
# name = "Prod - GKE - OpenCommunityDatathon namespace monthly budget"
|
||||
# amount = 0
|
||||
# currency_code = "EUR"
|
||||
# thresholds = {
|
||||
# current = [0.5, 0.8, 1.0]
|
||||
# forecasted = []
|
||||
# }
|
||||
# labels = {
|
||||
# "k8s-label/d4science-namespace" = "open-community-datathon"
|
||||
# }
|
||||
# calendar_period = "MONTH"
|
||||
# resource_ancestors = [local.folders.prod.id]
|
||||
# notification_channels = [local.monitoring.channels["budget-alerting"]]
|
||||
# }
|
||||
|
||||
module "d4science-prod-budget-script-sa" {
|
||||
source = "../assets/modules-fabric/v26/iam-service-account"
|
||||
|
||||
project_id = module.d4science-prod-budget-script-project.project_id
|
||||
name = "d4science-prod-budget-cf-sa"
|
||||
|
||||
iam = {
|
||||
"roles/iam.serviceAccountTokenCreator" = ["group:foundationreply@d4science.org"]
|
||||
#Impersonate service accounts (create OAuth2 access tokens, sign blobs or JWTs, etc).
|
||||
}
|
||||
|
||||
iam_billing_roles = {
|
||||
"${local.billing_account_id}" = ["roles/billing.viewer"]
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_vpc_access_connector" "d4science-prod-budget-script-vpc-connector" {
|
||||
project = module.d4science-prod-budget-script-project.project_id
|
||||
name = "d4science-prod-cf-vpc-con"
|
||||
region = var.gke_region
|
||||
subnet {
|
||||
name = "d4science-prod-ew4-vpc-con-sub"
|
||||
project_id = local.networking.spoke-prod-project.project_id
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
gke_region = "europe-west4"
|
||||
gke_subnet_id = "europe-west4/d4science-prod-ew4-vre-sub"
|
||||
gke_master_auth_networks = {
|
||||
"cnr-d4science-1" = "146.48.28.0/22",
|
||||
"cnr-d4science-2" = "146.48.122.0/23",
|
||||
"cnr-d4science-3" = "145.90.225.224/27",
|
||||
# "cnr-d4science-4" = "2001:610:450:80::/64",
|
||||
"reply-vpn-1" = "91.218.224.5/32",
|
||||
"reply-vpn-2" = "91.218.224.15/32",
|
||||
"reply-vpn-3" = "91.218.226.5/32",
|
||||
"reply-vpn-4" = "91.218.226.15/32",
|
||||
"budget-cf-vpc-access" = "10.254.2.0/28"
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
variable "outputs_location" {
|
||||
description = "Assets path location relative to the module"
|
||||
type = string
|
||||
default = "../assets"
|
||||
}
|
||||
|
||||
variable "gke_subnet_id" {
|
||||
description = "Id of the subnet where gke cluster must reside"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "gke_region" {
|
||||
description = "Region where gke cluster is located"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "gke_master_auth_networks" {
|
||||
description = "Networks which are allowed to access gke control plane"
|
||||
type = map(string)
|
||||
default = {}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
module "d4science-prod-vre-gke-cluster" {
|
||||
source = "../assets/modules-fabric/v26/gke-cluster-standard"
|
||||
project_id = module.d4science-prod-vre-project.project_id
|
||||
name = "d4science-prod-vre-gke-cluster"
|
||||
location = var.gke_region
|
||||
vpc_config = {
|
||||
network = local.networking.spoke-prod-project.network
|
||||
subnetwork = local.networking.spoke-prod-project.subnets[var.gke_subnet_id].self_link
|
||||
secondary_range_names = {
|
||||
pods = "pods"
|
||||
services = "services"
|
||||
}
|
||||
master_authorized_ranges = var.gke_master_auth_networks
|
||||
master_ipv4_cidr_block = "10.249.0.0/28"
|
||||
}
|
||||
private_cluster_config = {
|
||||
enable_private_endpoint = false
|
||||
master_global_access = false
|
||||
}
|
||||
enable_features = {
|
||||
dataplane_v2 = true
|
||||
workload_identity = true
|
||||
cost_management = true
|
||||
}
|
||||
enable_addons = {
|
||||
gce_persistent_disk_csi_driver = true
|
||||
gcp_filestore_csi_driver = true
|
||||
horizontal_pod_autoscaling = true
|
||||
http_load_balancing = true
|
||||
}
|
||||
logging_config = {
|
||||
enable_workloads_logs = true
|
||||
enable_api_server_logs = true
|
||||
enable_scheduler_logs = true
|
||||
enable_controller_manager_logs = true
|
||||
}
|
||||
monitoring_config = {
|
||||
enable_api_server_metrics = true
|
||||
enable_controller_manager_metrics = true
|
||||
enable_scheduler_metrics = true
|
||||
enable_daemonset_metrics = true
|
||||
enable_deployment_metrics = true
|
||||
enable_hpa_metrics = true
|
||||
enable_pod_metrics = true
|
||||
enable_statefulset_metrics = true
|
||||
enable_storage_metrics = true
|
||||
}
|
||||
maintenance_config = {
|
||||
daily_window_start_time = null
|
||||
recurring_window = {
|
||||
start_time = "2024-02-17T01:00:00Z"
|
||||
end_time = "2024-02-17T05:00:00Z"
|
||||
recurrence = "FREQ=WEEKLY;BYDAY=SA,SU,MO"
|
||||
}
|
||||
}
|
||||
labels = {
|
||||
environment = "prod"
|
||||
}
|
||||
}
|
||||
|
||||
module "d4science-prod-vre-gke-nodepool" {
|
||||
source = "../assets/modules-fabric/v26/gke-nodepool"
|
||||
project_id = module.d4science-prod-vre-project.project_id
|
||||
cluster_name = module.d4science-prod-vre-gke-cluster.name
|
||||
location = var.gke_region
|
||||
name = "d4science-prod-vre-gke-nodepool"
|
||||
service_account = {
|
||||
create = true
|
||||
email = "d4science-prod-vre-nodepoolsa"
|
||||
oauth_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
|
||||
}
|
||||
node_config = {
|
||||
machine_type = "custom-20-73728"
|
||||
gcfs = true
|
||||
}
|
||||
nodepool_config = {
|
||||
autoscaling = {
|
||||
max_node_count = 30
|
||||
min_node_count = 1
|
||||
}
|
||||
management = {
|
||||
auto_repair = true
|
||||
auto_upgrade = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module "d4science_prod_artifact_registry" {
|
||||
source = "../assets/modules-fabric/v26/artifact-registry"
|
||||
project_id = module.d4science-prod-vre-project.project_id
|
||||
location = var.gke_region
|
||||
name = "d4science-prod-images"
|
||||
iam = {
|
||||
"roles/artifactregistry.reader" = [module.d4science-prod-vre-gke-nodepool.service_account_iam_email]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
module "d4science-prod-vre-project" {
|
||||
source = "../assets/modules-fabric/v26/project"
|
||||
|
||||
name = "d4science-prod-vre-prj"
|
||||
prefix = local.prefix
|
||||
billing_account = local.billing_account_id
|
||||
parent = local.folders.prod.id
|
||||
labels = local.labels
|
||||
|
||||
auto_create_network = false
|
||||
project_create = true
|
||||
|
||||
services = [
|
||||
"cloudresourcemanager.googleapis.com", #required for SA impersonification
|
||||
"iam.googleapis.com", #required for IAM and SA impersonification
|
||||
"container.googleapis.com", #required for GKE
|
||||
"artifactregistry.googleapis.com", #required for Artifact registry
|
||||
"pubsub.googleapis.com", #required for PubSub
|
||||
"billingbudgets.googleapis.com", #required for Billing alerting
|
||||
"certificatemanager.googleapis.com", #required for certificates
|
||||
"secretmanager.googleapis.com", #required for secrets
|
||||
"logging.googleapis.com", #required for logging
|
||||
"file.googleapis.com", #required for Filestore
|
||||
"servicenetworking.googleapis.com",
|
||||
"containerfilesystem.googleapis.com" #required for image streaming in GKE
|
||||
]
|
||||
|
||||
iam = {
|
||||
"roles/owner" = [
|
||||
local.service_accounts.project_factory_prod,
|
||||
]
|
||||
"roles/container.developer" = ["serviceAccount:${module.d4science-prod-budget-script-sa.email}"]
|
||||
}
|
||||
|
||||
group_iam = {
|
||||
"foundationreply@d4science.org" = [
|
||||
"roles/owner"
|
||||
]
|
||||
}
|
||||
|
||||
logging_sinks = {
|
||||
gke = {
|
||||
destination = local.monitoring.bucket-sink.id
|
||||
filter = "resource.type=\"k8s_cluster\""
|
||||
type = "logging"
|
||||
unique_writer = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module "d4science-prod-vre-addresses" {
|
||||
source = "../assets/modules-fabric/v26/net-address"
|
||||
project_id = module.d4science-prod-vre-project.project_id
|
||||
global_addresses = ["d4science-prod-vre-address-ext"]
|
||||
}
|
||||
|
||||
module "d4science-prod-vre-ssl-secret" {
|
||||
source = "../assets/modules-fabric/v26/secret-manager"
|
||||
project_id = module.d4science-prod-vre-project.project_id
|
||||
secrets = {
|
||||
# Must be uploaded manually
|
||||
d4science-prod-vre-ssl-private-key = ["europe-west4"],
|
||||
d4science-prod-vre-ssl-public-cert = ["europe-west4"]
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_ssl_policy" "d4science-prod-vre-ssl-policy-1_2-modern" {
|
||||
project = module.d4science-prod-vre-project.project_id
|
||||
name = "d4science-prod-ssl-policy-1-2-modern"
|
||||
profile = "MODERN"
|
||||
min_tls_version = "TLS_1_2"
|
||||
}
|
||||
|
||||
resource "google_compute_security_policy" "d4science-prod-vre-armor-policy" {
|
||||
project = module.d4science-prod-vre-project.project_id
|
||||
name = "d4science-prod-vre-armor-policy"
|
||||
adaptive_protection_config {
|
||||
layer_7_ddos_defense_config {
|
||||
enable = true
|
||||
}
|
||||
}
|
||||
|
||||
rule {
|
||||
action = "deny(403)"
|
||||
description = "Block RU"
|
||||
priority = "1000"
|
||||
match {
|
||||
expr {
|
||||
expression = "origin.region_code == 'RU'"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rule {
|
||||
action = "allow"
|
||||
priority = "2147483647"
|
||||
match {
|
||||
versioned_expr = "SRC_IPS_V1"
|
||||
config {
|
||||
src_ip_ranges = ["*"]
|
||||
}
|
||||
}
|
||||
description = "default rule"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
locals {
|
||||
organization_vars = jsondecode(file("../assets/tfvars/00-organization.auto.tfvars.json"))
|
||||
networking_vars = jsondecode(file("../assets/tfvars/01-networking.auto.tfvars.json"))
|
||||
}
|
||||
locals {
|
||||
|
||||
billing_account_id = local.organization_vars.billing_account_id
|
||||
organization = local.organization_vars.organization
|
||||
prefix = local.organization_vars.prefix
|
||||
labels = local.organization_vars.labels
|
||||
groups = local.organization_vars.groups
|
||||
|
||||
# Global variables
|
||||
|
||||
seed-project = {
|
||||
project_id = local.organization_vars.seed-project.project_id
|
||||
project_number = local.organization_vars.seed-project.project_number
|
||||
}
|
||||
|
||||
service_accounts = {
|
||||
networking = local.organization_vars.service_accounts.networking
|
||||
security = local.organization_vars.service_accounts.security
|
||||
project_factory_test = local.organization_vars.service_accounts.project_factory_test
|
||||
}
|
||||
|
||||
networking = {
|
||||
spoke-test-project = local.networking_vars.spoke-test
|
||||
}
|
||||
|
||||
monitoring = {
|
||||
bucket-sink = local.organization_vars.monitoring.bucket-sink-test
|
||||
project_id = local.organization_vars.monitoring.project_id
|
||||
channels = local.organization_vars.monitoring.channels
|
||||
}
|
||||
|
||||
# Folders
|
||||
folders = {
|
||||
test = {
|
||||
id = local.organization_vars.folders.test.id
|
||||
name = local.organization_vars.folders.test.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
terraform {
|
||||
backend "gcs" {
|
||||
bucket = "d4science-test-ew8-foundation-tfprj-bkt"
|
||||
impersonate_service_account = "d4science-test-tfprj-sa@d4science-com-automation-prj.iam.gserviceaccount.com"
|
||||
}
|
||||
|
||||
required_version = "~> 1.6.5"
|
||||
|
||||
required_providers {
|
||||
google = {
|
||||
source = "hashicorp/google"
|
||||
version = "4.84.0"
|
||||
}
|
||||
google-beta = {
|
||||
source = "hashicorp/google-beta"
|
||||
version = "4.84.0"
|
||||
}
|
||||
local = {
|
||||
source = "hashicorp/local"
|
||||
version = "~> 2.4.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "google" {
|
||||
impersonate_service_account = "d4science-test-tfprj-sa@d4science-com-automation-prj.iam.gserviceaccount.com"
|
||||
}
|
||||
|
||||
provider "google-beta" {
|
||||
impersonate_service_account = "d4science-test-tfprj-sa@d4science-com-automation-prj.iam.gserviceaccount.com"
|
||||
}
|
||||
|
||||
provider "google" {
|
||||
alias = "no-impersonate"
|
||||
}
|
||||
|
||||
provider "local" {}
|
|
@ -0,0 +1,177 @@
|
|||
module "d4science-test-budget-script-project" {
|
||||
source = "../assets/modules-fabric/v26/project"
|
||||
|
||||
name = "d4science-test-script-prj"
|
||||
prefix = local.prefix
|
||||
billing_account = local.billing_account_id
|
||||
parent = local.folders.test.id
|
||||
labels = local.labels
|
||||
|
||||
auto_create_network = false
|
||||
project_create = true
|
||||
|
||||
services = [
|
||||
"cloudresourcemanager.googleapis.com", #required for SA impersonification
|
||||
"iam.googleapis.com", #required for IAM and SA impersonification
|
||||
"cloudfunctions.googleapis.com", #required for Cloud Functions
|
||||
"pubsub.googleapis.com", #required for PubSub
|
||||
"billingbudgets.googleapis.com", #required for Billing alerting,
|
||||
"run.googleapis.com", #required for Cloud Functions 2nd gen
|
||||
"cloudbuild.googleapis.com", #required for Cloud Functions 2nd gen
|
||||
"eventarc.googleapis.com", #required for Cloud Functions 2nd gen
|
||||
"vpcaccess.googleapis.com", #required for Serverless VPC access
|
||||
]
|
||||
|
||||
iam = {
|
||||
"roles/owner" = [
|
||||
local.service_accounts.project_factory_test,
|
||||
]
|
||||
}
|
||||
|
||||
group_iam = {
|
||||
"foundationreply@d4science.org" = [
|
||||
"roles/owner"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
module "d4science-test-budget-alert-topic" {
|
||||
source = "../assets/modules-fabric/v26/pubsub"
|
||||
project_id = module.d4science-test-budget-script-project.project_id
|
||||
name = "d4science-test-budget-alerting-topic"
|
||||
}
|
||||
|
||||
# module "d4science-test-billing-budget-overall" {
|
||||
# source = "../assets/modules-custom/billing-budget"
|
||||
# billing_account = local.billing_account_id
|
||||
# name = "Test - Overall budget"
|
||||
# amount = 1000
|
||||
# currency_code = "EUR"
|
||||
# thresholds = {
|
||||
# current = [0.5, 0.75, 1.0]
|
||||
# forecasted = []
|
||||
# }
|
||||
|
||||
# projects = [
|
||||
# "projects/${local.networking.spoke-test-project.number}",
|
||||
# "projects/${module.d4science-test-budget-script-project.number}",
|
||||
# "projects/${module.d4science-test-vre-project.number}"
|
||||
# ]
|
||||
# notification_channels = [local.monitoring.channels["budget-alerting"]]
|
||||
# }
|
||||
|
||||
# module "d4science-test-billing-budget-jupyter-hub" {
|
||||
# source = "../assets/modules-custom/billing-budget"
|
||||
# billing_account = local.billing_account_id
|
||||
# name = "Test - GKE - JupyterHub namespace budget"
|
||||
# amount = 100
|
||||
# currency_code = "EUR"
|
||||
# thresholds = {
|
||||
# current = [0.5, 0.75, 1.0]
|
||||
# forecasted = []
|
||||
# }
|
||||
# labels = {
|
||||
# "k8s-namespace" = "jupyter-hub"
|
||||
# }
|
||||
# resource_ancestors = [local.folders.test.id]
|
||||
# notification_channels = [local.monitoring.channels["budget-alerting"]]
|
||||
# pubsub_topic = module.d4science-test-budget-alert-topic.id # Attention: this cannot be done without temporarily deactivating the org policy iam.allowedPolicyMemberDomains
|
||||
# }
|
||||
|
||||
# module "d4science-test-billing-budget-blue-cloud" {
|
||||
# source = "../assets/modules-custom/billing-budget"
|
||||
# billing_account = local.billing_account_id
|
||||
# name = "Test - GKE - BlueCloud namespace budget"
|
||||
# amount = 100
|
||||
# currency_code = "EUR"
|
||||
# thresholds = {
|
||||
# current = [0.5, 0.75, 1.0]
|
||||
# forecasted = []
|
||||
# }
|
||||
# labels = {
|
||||
# "k8s-namespace" = "blue-cloud"
|
||||
# }
|
||||
# resource_ancestors = [local.folders.test.id]
|
||||
# notification_channels = [local.monitoring.channels["budget-alerting"]]
|
||||
# pubsub_topic = module.d4science-test-budget-alert-topic.id # Attention: this cannot be done without temporarily deactivating the org policy iam.allowedPolicyMemberDomains
|
||||
# }
|
||||
|
||||
# module "d4science-test-billing-budget-i-marine" {
|
||||
# source = "../assets/modules-custom/billing-budget"
|
||||
# billing_account = local.billing_account_id
|
||||
# name = "Test - GKE - iMarine namespace budget"
|
||||
# amount = 100
|
||||
# currency_code = "EUR"
|
||||
# thresholds = {
|
||||
# current = [0.5, 0.75, 1.0]
|
||||
# forecasted = []
|
||||
# }
|
||||
# labels = {
|
||||
# "k8s-namespace" = "i-marine"
|
||||
# }
|
||||
# resource_ancestors = [local.folders.test.id]
|
||||
# notification_channels = [local.monitoring.channels["budget-alerting"]]
|
||||
# pubsub_topic = module.d4science-test-budget-alert-topic.id # Attention: this cannot be done without temporarily deactivating the org policy iam.allowedPolicyMemberDomains
|
||||
# }
|
||||
|
||||
# module "d4science-test-billing-budget-so-big-data" {
|
||||
# source = "../assets/modules-custom/billing-budget"
|
||||
# billing_account = local.billing_account_id
|
||||
# name = "Test - GKE - SoBigData namespace budget"
|
||||
# amount = 100
|
||||
# currency_code = "EUR"
|
||||
# thresholds = {
|
||||
# current = [0.5, 0.75, 1.0]
|
||||
# forecasted = []
|
||||
# }
|
||||
# labels = {
|
||||
# "k8s-namespace" = "so-big-data"
|
||||
# }
|
||||
# resource_ancestors = [local.folders.test.id]
|
||||
# notification_channels = [local.monitoring.channels["budget-alerting"]]
|
||||
# pubsub_topic = module.d4science-test-budget-alert-topic.id # Attention: this cannot be done without temporarily deactivating the org policy iam.allowedPolicyMemberDomains
|
||||
# }
|
||||
|
||||
# module "d4science-test-billing-budget-open-community" {
|
||||
# source = "../assets/modules-custom/billing-budget"
|
||||
# billing_account = local.billing_account_id
|
||||
# name = "Test - GKE - OpenCommunity namespace budget"
|
||||
# amount = 100
|
||||
# currency_code = "EUR"
|
||||
# thresholds = {
|
||||
# current = [0.5, 0.75, 1.0]
|
||||
# forecasted = []
|
||||
# }
|
||||
# labels = {
|
||||
# "k8s-namespace" = "open-community"
|
||||
# }
|
||||
# resource_ancestors = [local.folders.test.id]
|
||||
# notification_channels = [local.monitoring.channels["budget-alerting"]]
|
||||
# pubsub_topic = module.d4science-test-budget-alert-topic.id # Attention: this cannot be done without temporarily deactivating the org policy iam.allowedPolicyMemberDomains
|
||||
# }
|
||||
|
||||
module "d4science-test-budget-script-sa" {
|
||||
source = "../assets/modules-fabric/v26/iam-service-account"
|
||||
|
||||
project_id = module.d4science-test-budget-script-project.project_id
|
||||
name = "d4science-test-budget-cf-sa"
|
||||
|
||||
iam = {
|
||||
"roles/iam.serviceAccountTokenCreator" = ["group:foundationreply@d4science.org"]
|
||||
#Impersonate service accounts (create OAuth2 access tokens, sign blobs or JWTs, etc).
|
||||
}
|
||||
|
||||
iam_billing_roles = {
|
||||
"${local.billing_account_id}" = ["roles/billing.viewer"]
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_vpc_access_connector" "d4science-test-budget-script-vpc-connector" {
|
||||
project = module.d4science-test-budget-script-project.project_id
|
||||
name = "d4science-test-cf-vpc-con"
|
||||
region = var.gke_region
|
||||
subnet {
|
||||
name = "d4science-test-ew8-vpc-con-sub"
|
||||
project_id = local.networking.spoke-test-project.project_id
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
gke_region = "europe-west8"
|
||||
gke_subnet_id = "europe-west8/d4science-test-ew8-vre-sub"
|
||||
gke_master_auth_networks = {
|
||||
"cnr-d4science-1" = "146.48.28.0/22",
|
||||
"cnr-d4science-2" = "146.48.122.0/23",
|
||||
"cnr-d4science-3" = "145.90.225.224/27",
|
||||
# "cnr-d4science-4" = "2001:610:450:80::/64",
|
||||
"reply-vpn-1" = "91.218.224.5/32",
|
||||
"reply-vpn-2" = "91.218.224.15/32",
|
||||
"reply-vpn-3" = "91.218.226.5/32",
|
||||
"reply-vpn-4" = "91.218.226.15/32",
|
||||
"budget-cf-vpc-access" = "10.254.66.0/28"
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
variable "outputs_location" {
|
||||
description = "Assets path location relative to the module"
|
||||
type = string
|
||||
default = "../assets"
|
||||
}
|
||||
|
||||
variable "gke_subnet_id" {
|
||||
description = "Id of the subnet where gke cluster must reside"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "gke_region" {
|
||||
description = "Region where gke cluster is located"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "gke_master_auth_networks" {
|
||||
description = "Networks which are allowed to access gke control plane"
|
||||
type = map(string)
|
||||
default = {}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
module "d4science-test-vre-gke-cluster" {
|
||||
source = "../assets/modules-fabric/v26/gke-cluster-standard"
|
||||
project_id = module.d4science-test-vre-project.project_id
|
||||
name = "d4science-test-vre-gke-cluster"
|
||||
location = var.gke_region
|
||||
vpc_config = {
|
||||
network = local.networking.spoke-test-project.network
|
||||
subnetwork = local.networking.spoke-test-project.subnets[var.gke_subnet_id].self_link
|
||||
secondary_range_names = {
|
||||
pods = "pods"
|
||||
services = "services"
|
||||
}
|
||||
master_authorized_ranges = var.gke_master_auth_networks
|
||||
master_ipv4_cidr_block = "10.249.0.64/28"
|
||||
}
|
||||
private_cluster_config = {
|
||||
enable_private_endpoint = false
|
||||
master_global_access = false
|
||||
}
|
||||
enable_features = {
|
||||
dataplane_v2 = true
|
||||
workload_identity = true
|
||||
cost_management = true
|
||||
}
|
||||
enable_addons = {
|
||||
gce_persistent_disk_csi_driver = true
|
||||
gcp_filestore_csi_driver = true
|
||||
horizontal_pod_autoscaling = true
|
||||
http_load_balancing = true
|
||||
}
|
||||
logging_config = {
|
||||
enable_workloads_logs = true
|
||||
enable_api_server_logs = true
|
||||
enable_scheduler_logs = true
|
||||
enable_controller_manager_logs = true
|
||||
}
|
||||
monitoring_config = {
|
||||
enable_api_server_metrics = true
|
||||
enable_controller_manager_metrics = true
|
||||
enable_scheduler_metrics = true
|
||||
enable_daemonset_metrics = true
|
||||
enable_deployment_metrics = true
|
||||
enable_hpa_metrics = true
|
||||
enable_pod_metrics = true
|
||||
enable_statefulset_metrics = true
|
||||
enable_storage_metrics = true
|
||||
}
|
||||
labels = {
|
||||
environment = "test"
|
||||
}
|
||||
}
|
||||
|
||||
module "d4science-test-vre-gke-nodepool" {
|
||||
source = "../assets/modules-fabric/v26/gke-nodepool"
|
||||
project_id = module.d4science-test-vre-project.project_id
|
||||
cluster_name = module.d4science-test-vre-gke-cluster.name
|
||||
location = var.gke_region
|
||||
name = "d4science-test-vre-gke-nodepool"
|
||||
service_account = {
|
||||
create = true
|
||||
email = "d4science-test-vre-nodepoolsa"
|
||||
oauth_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
|
||||
}
|
||||
node_config = {
|
||||
machine_type = "e2-medium"
|
||||
gcfs = true
|
||||
}
|
||||
nodepool_config = {
|
||||
autoscaling = {
|
||||
max_node_count = 2
|
||||
min_node_count = 0
|
||||
}
|
||||
management = {
|
||||
auto_repair = true
|
||||
auto_upgrade = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module "d4science_test_artifact_registry" {
|
||||
source = "../assets/modules-fabric/v26/artifact-registry"
|
||||
project_id = module.d4science-test-vre-project.project_id
|
||||
location = var.gke_region
|
||||
name = "d4science-test-images"
|
||||
iam = {
|
||||
"roles/artifactregistry.reader" = [module.d4science-test-vre-gke-nodepool.service_account_iam_email]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
module "d4science-test-vre-project" {
|
||||
source = "../assets/modules-fabric/v26/project"
|
||||
|
||||
name = "d4science-test-vre-prj"
|
||||
prefix = local.prefix
|
||||
billing_account = local.billing_account_id
|
||||
parent = local.folders.test.id
|
||||
labels = local.labels
|
||||
|
||||
auto_create_network = false
|
||||
project_create = true
|
||||
|
||||
services = [
|
||||
"cloudresourcemanager.googleapis.com", #required for SA impersonification
|
||||
"iam.googleapis.com", #required for IAM and SA impersonification
|
||||
"container.googleapis.com", #required for GKE
|
||||
"artifactregistry.googleapis.com", #required for Artifact registry
|
||||
"pubsub.googleapis.com", #required for PubSub
|
||||
"billingbudgets.googleapis.com", #required for Billing alerting
|
||||
"certificatemanager.googleapis.com", #required for certificates
|
||||
"secretmanager.googleapis.com", #required for secrets
|
||||
"logging.googleapis.com", #required for logging
|
||||
"file.googleapis.com", #required for Filestore
|
||||
"servicenetworking.googleapis.com",
|
||||
"containerfilesystem.googleapis.com" #required for image streaming in GKE
|
||||
]
|
||||
|
||||
iam = {
|
||||
"roles/owner" = [
|
||||
local.service_accounts.project_factory_test,
|
||||
]
|
||||
"roles/container.developer" = ["serviceAccount:${module.d4science-test-budget-script-sa.email}"]
|
||||
}
|
||||
|
||||
group_iam = {
|
||||
"foundationreply@d4science.org" = [
|
||||
"roles/owner"
|
||||
]
|
||||
}
|
||||
|
||||
logging_sinks = {
|
||||
gke = {
|
||||
destination = local.monitoring.bucket-sink.id
|
||||
filter = "resource.type=\"k8s_cluster\""
|
||||
type = "logging"
|
||||
unique_writer = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module "d4science-test-vre-addresses" {
|
||||
source = "../assets/modules-fabric/v26/net-address"
|
||||
project_id = module.d4science-test-vre-project.project_id
|
||||
global_addresses = ["d4science-test-vre-address-ext"]
|
||||
}
|
||||
|
||||
module "d4science-test-vre-ssl-secret" {
|
||||
source = "../assets/modules-fabric/v26/secret-manager"
|
||||
project_id = module.d4science-test-vre-project.project_id
|
||||
secrets = {
|
||||
# Must be uploaded manually
|
||||
d4science-test-vre-ssl-private-key = ["europe-west8"],
|
||||
d4science-test-vre-ssl-public-cert = ["europe-west8"]
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_ssl_policy" "d4science-test-vre-ssl-policy-1_2-modern" {
|
||||
project = module.d4science-test-vre-project.project_id
|
||||
name = "d4science-test-ssl-policy-1-2-modern"
|
||||
profile = "MODERN"
|
||||
min_tls_version = "TLS_1_2"
|
||||
}
|
||||
|
||||
resource "google_compute_security_policy" "d4science-test-vre-armor-policy" {
|
||||
project = module.d4science-test-vre-project.project_id
|
||||
name = "d4science-test-vre-armor-policy"
|
||||
adaptive_protection_config {
|
||||
layer_7_ddos_defense_config {
|
||||
enable = true
|
||||
}
|
||||
}
|
||||
|
||||
rule {
|
||||
action = "deny(403)"
|
||||
description = "Block RU"
|
||||
priority = "1000"
|
||||
match {
|
||||
expr {
|
||||
expression = "origin.region_code == 'RU'"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rule {
|
||||
action = "allow"
|
||||
priority = "2147483647"
|
||||
match {
|
||||
versioned_expr = "SRC_IPS_V1"
|
||||
config {
|
||||
src_ip_ranges = ["*"]
|
||||
}
|
||||
}
|
||||
description = "default rule"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
gcloud auth application-default login
|
||||
|
||||
## D4Science Foundation
|
||||
This repository contains all the folder structure for D4Science foundation to support VRE applications.
|
||||
|
||||
### PRJ Details
|
||||
- Billing account: `018258-BCA804-E9D4C6`
|
||||
- Tenant:
|
||||
- `d4science`
|
||||
|
||||
## Folder structure
|
||||
```
|
||||
.
|
||||
├── 00-organization
|
||||
├── 01-networking
|
||||
│ └── data
|
||||
│ └── subnets
|
||||
├── 02-security
|
||||
│ └── firewall
|
||||
│ └── rules
|
||||
│ ├── 00-spoke-test
|
||||
│ ├── 01-spoke-prod
|
||||
├── 03-monitoring
|
||||
│ └── data
|
||||
├── 04-project-factory-test
|
||||
├── 04-project-factory-prod
|
||||
└── assets
|
||||
├── providers
|
||||
└── tfvars
|
||||
```
|
||||
|
||||
## Naming Convention
|
||||
The naming convention for this foundation project is the following one:
|
||||
`<tenant>-<env>-<name>-prj`
|
||||
|
||||
- tenant: `d4science`
|
||||
- env:
|
||||
- test
|
||||
- prod
|
||||
- name: project name
|
||||
|
||||
### Resources
|
||||
The naming convention to use for the resources of this project is:
|
||||
|
||||
`<tenant>-<env>-<location (optional)-<app. initiative (optional)>-<unique id>-<res. short name>`
|
||||
|
||||
#### Resource name examples
|
||||
- Service Account: `d4science-dev-foundation-tfnet-sa`
|
||||
- VPC: `d4science-dev-glb-spoke-vpc` (location glb = global)
|
||||
- Subnet: `d4science-dev-ew8-01-sub` (unique id = 01 used as incremental counter)
|
||||
- Bucket: `d4science-org-ew8-foundation-tforg-bkt`
|
||||
- peering: `d4science-prod-hubspoke-peer`
|
||||
|
||||
### Firewall Rule
|
||||
The naming convention to use for the firewall rules of this project is:
|
||||
|
||||
`<tenant>-<env>-glb-<app. initiative>-<ing/egr>-<alw/den>-<unique id>-<res. short name>`
|
||||
|
||||
#### FW rule example
|
||||
- FW rule: `d4science-test-glb-paytas-ing-alw-gke-fwr`
|
||||
|
||||
### General considerations
|
||||
- Avoid fields with "-"
|
||||
- unique id can be:
|
||||
- numbers (e.g. 01)
|
||||
- letters (e.g. abc)
|
||||
- both (e.g. abc01)
|
||||
|
||||
### SHORT NAMES
|
||||
#### Resources
|
||||
- SA: service account
|
||||
- BKT: bucket
|
||||
- PRJ: project
|
||||
- SUB: subnet
|
||||
- VPC: vpc
|
||||
- FWR: Firewall
|
|
@ -0,0 +1,89 @@
|
|||
# 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 -->
|
|
@ -0,0 +1,99 @@
|
|||
/**
|
||||
* 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
|
||||
resource_ancestors = var.resource_ancestors
|
||||
credit_types_treatment = var.credit_treatment
|
||||
services = var.services
|
||||
labels = var.labels
|
||||
calendar_period = var.calendar_period
|
||||
}
|
||||
|
||||
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
|
||||
currency_code = var.currency_code
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
output "budget" {
|
||||
description = "Budget resource."
|
||||
value = google_billing_budget.budget
|
||||
}
|
||||
|
||||
output "id" {
|
||||
description = "Fully qualified budget id."
|
||||
value = google_billing_budget.budget.id
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
/**
|
||||
* 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 "currency_code" {
|
||||
description = "The 3-letter currency code defined in ISO 4217."
|
||||
type = string
|
||||
default = "USD"
|
||||
}
|
||||
|
||||
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 "resource_ancestors" {
|
||||
description = "List of organizations of the form organizations/{organization_id} and/or folders of the form folders/{folder_id}, specifying that usage from only this set of ancestors should be included in the budget. Set to null to include all organizations and folders 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 "labels" {
|
||||
description = "A single label and value pair specifying that usage from only this set of labeled resources should be included in the budget. Set to null to include all labels."
|
||||
type = map(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."
|
||||
}
|
||||
}
|
||||
|
||||
variable "calendar_period" {
|
||||
description = "The calendar period the budget spans. It can be either MONTH, QUARTER or YEAR."
|
||||
type = string
|
||||
default = null
|
||||
validation {
|
||||
condition = var.calendar_period == null || (var.calendar_period == null ? true : contains(["MONTH", "QUARTER", "YEAR"], var.calendar_period))
|
||||
error_message = "Allowed values for calendar_period are \"MONTH\", \"QUARTER\", or \"YEAR\"."
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
# 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
|
||||
#
|
||||
# https://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.
|
||||
|
||||
terraform {
|
||||
required_version = ">= 1.4.4"
|
||||
required_providers {
|
||||
google = {
|
||||
source = "hashicorp/google"
|
||||
version = ">= 4.82.0" # tftest
|
||||
}
|
||||
google-beta = {
|
||||
source = "hashicorp/google-beta"
|
||||
version = ">= 4.82.0" # tftest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
#-------------------------------------------------------------------------------
|
||||
# DNS Zones for PRIVATE GOOGLE ACCESS
|
||||
#-------------------------------------------------------------------------------
|
||||
/* !!!!! README !!!!!
|
||||
* We use restricted.googleapis.com and additional domains linked to restricted APIS.
|
||||
* Private Google Apis (private.googleapis.com) has been left here for future use, if needed.
|
||||
*/
|
||||
locals {
|
||||
private_ips = ["199.36.153.8", "199.36.153.9", "199.36.153.10", "199.36.153.11"]
|
||||
restricted_ips = ["199.36.153.4", "199.36.153.5", "199.36.153.6", "199.36.153.7"]
|
||||
ips = var.config.private ? local.private_ips : local.restricted_ips
|
||||
name = var.config.private ? "private" : "restricted"
|
||||
|
||||
fixed_domains = {
|
||||
accounts = ["accounts.google.com"]
|
||||
appengine = ["appengine.google.com"]
|
||||
appspot = ["*.appspot.com"]
|
||||
artifact = ["*.gcr.io", "*.pkg.dev"]
|
||||
functions = ["*.cloudfunctions.net"]
|
||||
proxy = ["*.cloudproxy.app"]
|
||||
run = ["*.run.app"]
|
||||
composer = ["*.composer.cloud.google.com", "*.composer.googleusercontent.com"]
|
||||
datafusion = ["*.datafusion.cloud.google.com", "*.datafusion.googleusercontent.com"]
|
||||
dataproc = ["*.dataproc.cloud.google.com", "dataproc.cloud.google.com", "*.dataproc.googleusercontent.com", "dataproc.googleusercontent.com"]
|
||||
download = ["dl.google.com"]
|
||||
ad = ["*.googleadapis.com"]
|
||||
gstatic = ["*.gstatic.com"]
|
||||
lts = ["*.ltsapis.goog"]
|
||||
notebooks = ["*.notebooks.cloud.google.com", "*.notebooks.googleusercontent.com"]
|
||||
packages = ["packages.cloud.google.com"]
|
||||
pki = ["*.pki.goog"]
|
||||
source = ["source.developers.google.com"]
|
||||
}
|
||||
|
||||
enabled_fixed_domain = merge([
|
||||
for k, v in var.domains : {
|
||||
for i, d in local.fixed_domains[k] :
|
||||
"${k}-${i}" => d
|
||||
} if v
|
||||
]...)
|
||||
|
||||
enabled_custom_domain = merge([
|
||||
for k, v in var.custom_domains : {
|
||||
for i, d in v :
|
||||
"${k}-${i}" => d
|
||||
}
|
||||
]...)
|
||||
|
||||
enabled_domain = merge(local.enabled_fixed_domain, local.enabled_custom_domain)
|
||||
|
||||
}
|
||||
|
||||
#-------------------------------------
|
||||
# PRIVATE.GOOGLEAPIS.COM
|
||||
#-------------------------------------
|
||||
# DNS zone for PGA: *.googleapis.com.
|
||||
module "googleapis_zone" {
|
||||
source = "../../modules-fabric/v26/dns"
|
||||
project_id = var.project_id
|
||||
name = "${var.name}-pga-${local.name}-apis"
|
||||
zone_config = {
|
||||
domain = "googleapis.com."
|
||||
private = {
|
||||
client_networks = var.networks
|
||||
}
|
||||
}
|
||||
recordsets = {
|
||||
"A ${local.name}.googleapis.com." = { ttl = 300, records = local.ips }
|
||||
"CNAME *.googleapis.com." = { ttl = 300, records = ["${local.name}.googleapis.com."] }
|
||||
}
|
||||
}
|
||||
|
||||
# DNS zone for others APIs
|
||||
# All others api (different from *.googleapis.com)
|
||||
module "pga_apis_zone" {
|
||||
for_each = local.enabled_domain
|
||||
source = "../../modules-fabric/v26/dns"
|
||||
project_id = var.project_id
|
||||
name = "${var.name}-pga-${local.name}-api-${each.key}"
|
||||
zone_config = {
|
||||
domain = "${trimprefix(each.value, "*.")}."
|
||||
private = {
|
||||
client_networks = var.networks
|
||||
}
|
||||
}
|
||||
recordsets = {
|
||||
# We are adding wildcard even if not needed, as per documentation.
|
||||
"A ${trimprefix(each.value, "*.")}." = { ttl = 300, records = local.ips }
|
||||
"CNAME *.${trimprefix(each.value, "*.")}." = { ttl = 300, records = ["${trimprefix(each.value, "*.")}."] }
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_service_networking_peered_dns_domain" "apis-dns-peering" {
|
||||
for_each = can(var.config.value["service_networking_network"]) ? local.enabled_domain : {}
|
||||
project = var.project_id
|
||||
network = var.config.service_networking_network
|
||||
name = "${replace(trimprefix(each.value, "*."), ".", "-")}-domain"
|
||||
dns_suffix = "*.${trimprefix(each.value, "*.")}."
|
||||
}
|
||||
|
||||
resource "google_service_networking_peered_dns_domain" "googleapis-dns-peering" {
|
||||
count = can(var.config.value["service_networking_network"]) ? 1 : 0
|
||||
project = var.project_id
|
||||
name = "googleapis-domain"
|
||||
network = var.config.service_networking_network
|
||||
dns_suffix = "googleapis."
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
variable "domains" {
|
||||
description = "Declare which domains to redirect to PGA"
|
||||
type = object({
|
||||
accounts = optional(bool, false)
|
||||
appengine = optional(bool, false)
|
||||
appspot = optional(bool, false)
|
||||
artifact = optional(bool, false)
|
||||
functions = optional(bool, false)
|
||||
proxy = optional(bool, false)
|
||||
run = optional(bool, false)
|
||||
composer = optional(bool, false)
|
||||
datafusion = optional(bool, false)
|
||||
dataproc = optional(bool, false)
|
||||
download = optional(bool, false)
|
||||
ad = optional(bool, false)
|
||||
gstatic = optional(bool, false)
|
||||
lts = optional(bool, false)
|
||||
notebooks = optional(bool, false)
|
||||
packages = optional(bool, false)
|
||||
pki = optional(bool, false)
|
||||
source = optional(bool, false)
|
||||
})
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "custom_domains" {
|
||||
type = map(list(string))
|
||||
description = "Optional map {NAME}=>[{domain1},{domain2},...]"
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "config" {
|
||||
type = object({
|
||||
private = optional(bool, false)
|
||||
restricted = optional(bool, false)
|
||||
service_networking_network = optional(string)
|
||||
})
|
||||
description = "(optional) describe your variable"
|
||||
|
||||
validation {
|
||||
condition = var.config.private != var.config.restricted
|
||||
error_message = "One and only one between private and restricted must be set to true"
|
||||
}
|
||||
}
|
||||
|
||||
variable "project_id" {
|
||||
type = string
|
||||
description = "DNS zone project id"
|
||||
}
|
||||
|
||||
variable "name" {
|
||||
type = string
|
||||
description = "Name for the created resources"
|
||||
}
|
||||
|
||||
variable "networks" {
|
||||
type = list(string)
|
||||
description = "List of networks self-links for DNS zone"
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
# Terraform modules suite for Google Cloud
|
||||
|
||||
The modules collected in this folder are designed as a suite: they are meant to be composed together, and are designed to be forked and modified where use of third party code and sources is not allowed.
|
||||
|
||||
Modules try to stay close to the low level provider resources they encapsulate, and they all share a similar interface that combines management of one resource or set or resources, and the corresponding IAM bindings.
|
||||
|
||||
Authoritative IAM bindings are primarily used (e.g. `google_storage_bucket_iam_binding` for GCS buckets) so that each module is authoritative for specific roles on the resources it manages, and can neutralize or reconcile IAM changes made elsewhere.
|
||||
|
||||
Specific modules also offer support for non-authoritative bindings (e.g. `google_storage_bucket_iam_member` for service accounts), to allow granular permission management on resources that they don't manage directly.
|
||||
|
||||
These modules are not necessarily backward compatible. Changes breaking compatibility in modules are marked by major releases (but not all major releases contain breaking changes). Please be mindful when upgrading Fabric modules in existing Terraform setups, and always try to use versioned references in module sources so you can easily revert back to a previous version. Since the introduction of the `moved` block in Terraform we try to use it whenever possible to make updates non-breaking, but that does not cover all changes we might need to make.
|
||||
|
||||
These modules are used in the examples included in this repository. If you are using any of those examples in your own Terraform configuration, make sure that you are using the same version for all the modules, and switch module sources to GitHub format using references. The recommended approach to working with Fabric modules is the following:
|
||||
|
||||
- Fork the repository and own the fork. This will allow you to:
|
||||
- Evolve the existing modules.
|
||||
- Create your own modules.
|
||||
- Sync from the upstream repository to get all the updates.
|
||||
|
||||
- Use GitHub sources with refs to reference the modules. See an example below:
|
||||
|
||||
```terraform
|
||||
module "project" {
|
||||
source = "../assets/modules-fabric/v26/project"
|
||||
name = "my-project"
|
||||
billing_account = "123456-123456-123456"
|
||||
parent = "organizations/123456"
|
||||
}
|
||||
```
|
||||
|
||||
## Foundational modules
|
||||
|
||||
- [billing budget](./billing-budget)
|
||||
- [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)
|
||||
|
||||
## Networking modules
|
||||
|
||||
- [Address reservation](./net-address)
|
||||
- [Cloud Endpoints](./endpoints)
|
||||
- [DNS](./dns)
|
||||
- [DNS Response Policy](./dns-response-policy/)
|
||||
- [Firewall policy](./net-firewall-policy)
|
||||
- [External Application Load Balancer](./net-lb-app-ext/)
|
||||
- [External Passthrough Network Load Balancer](./net-lb-ext)
|
||||
- [Internal Application Load Balancer](./net-lb-app-int)
|
||||
- [Internal Passthrough Network Load Balancer](./net-lb-int)
|
||||
- [Internal Proxy Network Load Balancer](./net-lb-proxy-int)
|
||||
- [Internal ]
|
||||
- [NAT](./net-cloudnat)
|
||||
- [Service Directory](./service-directory)
|
||||
- [VPC](./net-vpc)
|
||||
- [VPC firewall](./net-vpc-firewall)
|
||||
- [VPN dynamic](./net-vpn-dynamic)
|
||||
- [VPC peering](./net-vpc-peering)
|
||||
- [VPN HA](./net-vpn-ha)
|
||||
- [VPN static](./net-vpn-static)
|
||||
|
||||
## Compute/Container
|
||||
|
||||
- [VM/VM group](./compute-vm)
|
||||
- [MIG](./compute-mig)
|
||||
- [COS container](./cloud-config-container/cos-generic-metadata/) (coredns/mysql/nva/onprem/squid)
|
||||
- [GKE autopilot cluster](./gke-cluster-autopilot)
|
||||
- [GKE standard cluster](./gke-cluster-standard)
|
||||
- [GKE hub](./gke-hub)
|
||||
- [GKE nodepool](./gke-nodepool)
|
||||
- [GCVE private cloud](./gcve-private-cloud)
|
||||
|
||||
## Data
|
||||
|
||||
- [AlloyDB instance](./alloydb-instance)
|
||||
- [BigQuery dataset](./bigquery-dataset)
|
||||
- [Bigtable instance](./bigtable-instance)
|
||||
- [Dataplex](./dataplex)
|
||||
- [Dataplex DataScan](./dataplex-datascan/)
|
||||
- [Cloud SQL instance](./cloudsql-instance)
|
||||
- [Data Catalog Policy Tag](./data-catalog-policy-tag)
|
||||
- [Datafusion](./datafusion)
|
||||
- [Dataproc](./dataproc)
|
||||
- [GCS](./gcs)
|
||||
- [Pub/Sub](./pubsub)
|
||||
|
||||
## Development
|
||||
|
||||
- [API Gateway](./api-gateway)
|
||||
- [Apigee](./apigee)
|
||||
- [Artifact Registry](./artifact-registry)
|
||||
- [Container Registry](./container-registry)
|
||||
- [Cloud Source Repository](./source-repository)
|
||||
|
||||
## Security
|
||||
|
||||
- [Binauthz](./binauthz/)
|
||||
- [KMS](./kms)
|
||||
- [SecretManager](./secret-manager)
|
||||
- [VPC Service Control](./vpc-sc)
|
||||
- [Secure Web Proxy](./net-swp)
|
||||
|
||||
## Serverless
|
||||
|
||||
- [Cloud Functions v1](./cloud-function-v1)
|
||||
- [Cloud Functions v2](./cloud-function-v2)
|
||||
- [Cloud Run](./cloud-run)
|
|
@ -0,0 +1,344 @@
|
|||
# Refactor IAM interface
|
||||
|
||||
**authors:** [Ludo](https://github.com/ludoo), [Julio](https://github.com/juliocc)
|
||||
**last modified:** August 17, 2023
|
||||
|
||||
## Status
|
||||
|
||||
Implemented in [#1595](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1595).
|
||||
Authoritative bindings type changed as per [#1622](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/issues/1622).
|
||||
|
||||
## Context
|
||||
|
||||
The IAM interface in our modules has evolved organically to progressively support more functionality, resulting in a large variable surface, lack of support for some key features like conditions, and some fragility for specific use cases.
|
||||
|
||||
We currently support, with uneven coverage across modules:
|
||||
|
||||
- authoritative `iam` in `ROLE => [PRINCIPALS]` format
|
||||
- authoritative `group_iam` in `GROUP => [ROLES]` format
|
||||
- legacy additive `iam_additive` in `ROLE => [PRINCIPALS]` format which breaks for dynamic values
|
||||
- legacy additive `iam_additive_members` in `PRINCIPAL => [ROLES]` format which breaks for dynamic values
|
||||
- new additive `iam_members` in `KEY => {role: ROLE, member: MEMBER, condition: CONDITION}` format which works with dynamic values and supports conditions
|
||||
- policy authoritative `iam_policy`
|
||||
- specific support for third party resource bindings in the service account module
|
||||
|
||||
## Proposal
|
||||
|
||||
### Authoritative bindings
|
||||
|
||||
These tend to work well in practice, and the current `iam` and `group_iam` variables are simple to use with good coverage across modules.
|
||||
|
||||
The only small use case that they do not cover is IAM conditions, which are easy to implement but would render the interface more verbose for the majority of cases where conditions are not needed.
|
||||
|
||||
The **proposal** for authoritative bindings is to
|
||||
|
||||
- leave the current interface in place (`iam` and `group_iam`)
|
||||
- expand coverage so that all modules who have iam resources expose both
|
||||
- add a new `iam_bindings` variable to support authoritative IAM with conditions
|
||||
|
||||
The new `iam_bindings` variable will look like this:
|
||||
|
||||
```hcl
|
||||
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 = {}
|
||||
}
|
||||
```
|
||||
|
||||
This variable will not be internally merged in modules with `iam` or `group_iam`.
|
||||
|
||||
### Additive bindings
|
||||
|
||||
Additive bindings have evolved to mimick authoritative ones, but the result is an interface which is bloated (no one uses `iam_additive_members`), and hard to understand and use without triggering dynamic errors. Coverage is also spotty and uneven across modules, and the interface needs to support aliasing of project service accounts in the project module to work around dynamic errors.
|
||||
|
||||
The `iam_additive` variable is used in a special patterns in data blueprints, to allow code to not mess up existing IAM bindings in an external project on destroy. This pattern only works in a limited set of cases, where principals are passed in via static variables or refer to "magic" static outputs in our modules. This is a simple example of the pattern:
|
||||
|
||||
```hcl
|
||||
locals {
|
||||
iam = {
|
||||
"roles/viewer" = [
|
||||
module.sa.iam_email,
|
||||
var.group.admins
|
||||
]
|
||||
}
|
||||
}
|
||||
module "project" {
|
||||
iam = (
|
||||
var.project_create == null ? {} : local.iam
|
||||
)
|
||||
iam_additive = (
|
||||
var.project_create != null ? {} : local.iam
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
The **proposal** for authoritative bindings is to
|
||||
|
||||
- remove `iam_additive` and `iam_additive_members` from the interface
|
||||
- add a new `iam_bindings_additive` variable
|
||||
|
||||
Once new variables are in place, migrate existing blueprints to using `iam_bindings_additive` using one of the two available patterns:
|
||||
|
||||
- the flat verbose one where bindings are declared in the module call
|
||||
- the more complex one that moves roles out to `locals` and uses them in `for` loops
|
||||
|
||||
The new variable will closely follow the type of the authoritative `iam_bindings` variable described above:
|
||||
|
||||
```hcl
|
||||
variable "iam_bindings_additive" {
|
||||
description = "Additive IAM bindings with support for conditions, in {KEY => { role = ROLE, members = [], condition = {}}} format."
|
||||
type = map(object({
|
||||
member = string
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
}
|
||||
```
|
||||
|
||||
### IAM policy
|
||||
|
||||
The **proposal** is to remove the IAM policy variable and resources, as its coverage is very uneven and we never used it in practice. This will also simplify data access log management, which is currently split between its own variable/resource and the IAM policy ones.
|
||||
|
||||
## Decision
|
||||
|
||||
The proposal above summarizes the state of discussions between the authors, and implementation will be tested.
|
||||
|
||||
## Consequences
|
||||
|
||||
### FAST
|
||||
|
||||
IAM implementation in the bootstrap stage and matching multitenant bootstrap has radically changed, with the addition of a new [`organization-iam.tf`](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/blob/master/fast/stages/0-bootstrap/organization-iam.tf) file which contains IAM binding definitions in an abstracted format, that is then converted to the specific formats required by the `iam`, `iam_bindings` and `iam_bindings_additive` variables.
|
||||
|
||||
This brings several advantages over the previous handling of IAM:
|
||||
|
||||
- authoritative and additive bindings are now grouped by principal in an easy to read and change format that serves as its own documentation
|
||||
- support for IAM conditions has removed the need for standalone resources and made the intent behind those more explicit
|
||||
- some subtle bugs on the intersection of user-specified bindings and internally-specified ones have been addressed
|
||||
|
||||
### Blueprints
|
||||
|
||||
A few data blueprints that leverage `iam_additive` have been refactored to use the new variable. This is most notable in data blueprints, where extra files have been added to the more complex examples like data foundations, to abstract IAM bindings in a way similar to what is described above for FAST.
|
||||
|
||||
## Implementation
|
||||
|
||||
The following sections provide a template for IAM-related variables and resources to ensure a consistent implementation of IAM across the repository. Use these code snippets to add IAM support to your module.
|
||||
|
||||
### Top-level module IAM
|
||||
|
||||
Use this template if your module manages a single instance of a given resource (e.g. a KMS keyring).
|
||||
|
||||
```terraform
|
||||
# variables.tf
|
||||
|
||||
variable "iam" {
|
||||
description = "IAM bindings in {ROLE => [MEMBERS]} format. Mutually exclusive with the access_* variables used for basic roles."
|
||||
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)
|
||||
}))
|
||||
}))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "iam_bindings_additive" {
|
||||
description = "Keyring individual additive IAM bindings. Keys are arbitrary."
|
||||
type = map(object({
|
||||
member = string
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
}))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
```
|
||||
|
||||
```terraform
|
||||
# iam.tf
|
||||
|
||||
resource "google_RESOURCE_TYPE_iam_binding" "authoritative" {
|
||||
for_each = var.iam
|
||||
role = each.key
|
||||
members = each.value
|
||||
// add extra attributes (e.g. resource id)
|
||||
}
|
||||
|
||||
resource "google_RESOURCE_TYPE_iam_binding" "bindings" {
|
||||
for_each = var.iam_bindings
|
||||
role = each.value.role
|
||||
members = each.value.members
|
||||
// add extra attributes (e.g. resource id)
|
||||
|
||||
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_RESOURCE_TYPE_iam_member" "bindings" {
|
||||
for_each = var.iam_bindings_additive
|
||||
role = each.value.role
|
||||
member = each.value.member
|
||||
// add extra attributes (e.g. resource id)
|
||||
|
||||
dynamic "condition" {
|
||||
for_each = each.value.condition == null ? [] : [""]
|
||||
content {
|
||||
expression = each.value.condition.expression
|
||||
title = each.value.condition.title
|
||||
description = each.value.condition.description
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Sub-resources IAM
|
||||
|
||||
Use this template if your module manages multiple instances of a resource (e.g. keys in KMS keyring).
|
||||
|
||||
```terraform
|
||||
# variables.tf
|
||||
variable "sub_resources" {
|
||||
type = map(object({
|
||||
# sub-resource configuration here
|
||||
|
||||
iam = optional(map(list(string)), {})
|
||||
iam_bindings = optional(map(object({
|
||||
members = list(string)
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
})), {})
|
||||
iam_bindings_additive = optional(map(object({
|
||||
member = string
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
})), {})
|
||||
}))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
```
|
||||
|
||||
```terraform
|
||||
# iam.tf
|
||||
locals {
|
||||
SUB_RESOURCE_iam = flatten([
|
||||
for k, v in var.SUB_RESOURCEs : [
|
||||
for role, members in v.iam : {
|
||||
key = k
|
||||
role = role
|
||||
members = members
|
||||
}
|
||||
]
|
||||
])
|
||||
SUB_RESOURCE_iam_bindings = merge([
|
||||
for k, v in var.SUB_RESOURCEs : {
|
||||
for binding_key, data in v.iam_bindings :
|
||||
binding_key => {
|
||||
SUB_RESOURCE = k
|
||||
role = data.role
|
||||
members = data.members
|
||||
condition = data.condition
|
||||
}
|
||||
}
|
||||
]...)
|
||||
SUB_RESOURCE_iam_bindings_additive = merge([
|
||||
for k, v in var.subresources : {
|
||||
for binding_key, data in v.iam_bindings_additive :
|
||||
binding_key => {
|
||||
SUB_RESOURCE = k
|
||||
role = data.role
|
||||
member = data.member
|
||||
condition = data.condition
|
||||
}
|
||||
}
|
||||
]...)
|
||||
}
|
||||
```
|
||||
|
||||
```terraform
|
||||
# iam.tf
|
||||
|
||||
resource "google_SUB_RESOURCE_iam_binding" "authoritative" {
|
||||
for_each = {
|
||||
for binding in local.SUB_RESOURCE_iam :
|
||||
"${binding.key}.${binding.role}" => binding
|
||||
}
|
||||
role = each.value.role
|
||||
members = each.value.members
|
||||
// add extra attributes (e.g. sub resource id)
|
||||
}
|
||||
|
||||
resource "google_SUB_RESOURCE_iam_binding" "bindings" {
|
||||
for_each = local.SUB_RESOURCE_iam_bindings
|
||||
role = each.value.role
|
||||
members = each.value.members
|
||||
// add extra attributes (e.g. sub resource id)
|
||||
|
||||
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_SUB_RESOURCE_iam_member" "members" {
|
||||
for_each = local.SUB_RESOURCE_iam_bindings_additive
|
||||
role = each.value.role
|
||||
member = each.value.member
|
||||
// add extra attributes (e.g. sub resource id)
|
||||
|
||||
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,3 @@
|
|||
# Fabric modules architectural documents
|
||||
|
||||
This folder contains assorted bits of documentation used to log current architectural choices, or past decisions. Format is inspired by [Michael Nygard's decision record template](https://github.com/joelparkerhenderson/architecture-decision-record/blob/main/templates/decision-record-template-by-michael-nygard/index.md).
|
|
@ -0,0 +1,35 @@
|
|||
# Google Cloud DNS Inbound Policy Addresses
|
||||
|
||||
This module allows discovering the addresses reserved in subnets when [DNS Inbound Policies](https://cloud.google.com/dns/docs/policies) are configured.
|
||||
|
||||
Since it's currently impossible to fetch those addresses using a GCP data source (see [this issue](https://github.com/hashicorp/terraform-provider-google/issues/3753) for more details), the workaround used here is to derive the authorization token from the Google provider, and do a direct HTTP call to the Compute API.
|
||||
|
||||
## Examples
|
||||
|
||||
```hcl
|
||||
module "dns-policy-addresses" {
|
||||
source = "./fabric/modules/__experimental/net-dns-policy-addresses"
|
||||
project_id = "myproject"
|
||||
regions = ["europe-west1", "europe-west3"]
|
||||
}
|
||||
# tftest skip (uses data sources)
|
||||
```
|
||||
|
||||
The output is a map with lists of addresses of type `DNS_RESOLVER` for each region specified in variables.
|
||||
|
||||
<!-- BEGIN TFDOC -->
|
||||
|
||||
## Variables
|
||||
|
||||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [project_id](variables.tf#L17) | Project id. | <code>string</code> | ✓ | |
|
||||
| [regions](variables.tf#L22) | Regions to fetch addresses from. | <code>list(string)</code> | | <code>["europe-west1"]</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
| name | description | sensitive |
|
||||
|---|---|:---:|
|
||||
| [addresses](outputs.tf#L24) | DNS inbound policy addresses per region. | |
|
||||
|
||||
<!-- END TFDOC -->
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* 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 {
|
||||
url = format(
|
||||
"https://content-compute.googleapis.com/compute/v1/projects/%s",
|
||||
var.project_id
|
||||
)
|
||||
}
|
||||
|
||||
data "google_client_config" "current" {}
|
||||
|
||||
data "http" "addresses" {
|
||||
for_each = toset(var.regions)
|
||||
url = "${local.url}/regions/${each.key}/addresses?filter=purpose%20%3D%20%22DNS_RESOLVER%22"
|
||||
request_headers = {
|
||||
Authorization = "Bearer ${data.google_client_config.current.access_token}"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* 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 {
|
||||
region_addresses = {
|
||||
for k, v in data.http.addresses : k => try(jsondecode(v.body), {})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
output "addresses" {
|
||||
description = "DNS inbound policy addresses per region."
|
||||
value = {
|
||||
for k, v in local.region_addresses : k => [
|
||||
for i in try(v.items, []) : i.address
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* 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 "project_id" {
|
||||
description = "Project id."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "regions" {
|
||||
description = "Regions to fetch addresses from."
|
||||
nullable = false
|
||||
type = list(string)
|
||||
default = ["europe-west1"]
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
# Network Endpoint Group Module
|
||||
|
||||
This modules allows creating zonal network endpoint groups.
|
||||
|
||||
Note: this module will integrated into a general-purpose load balancing module in the future.
|
||||
|
||||
## Example
|
||||
```hcl
|
||||
module "neg" {
|
||||
source = "./fabric/modules/__experimental/net-neg/"
|
||||
project_id = "myproject"
|
||||
name = "myneg"
|
||||
network = var.vpc.self_link
|
||||
subnetwork = var.subnet.self_link
|
||||
zone = "europe-west1-b"
|
||||
endpoints = [
|
||||
for instance in module.vm.instances :
|
||||
{
|
||||
instance = instance.name
|
||||
port = 80
|
||||
ip_address = instance.network_interface[0].network_ip
|
||||
}
|
||||
]
|
||||
}
|
||||
# tftest skip
|
||||
```
|
||||
<!-- BEGIN TFDOC -->
|
||||
|
||||
## Variables
|
||||
|
||||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [endpoints](variables.tf#L17) | List of (instance, port, address) of the NEG. | <code title="list(object({ instance = string port = number ip_address = string }))">list(object({…}))</code> | ✓ | |
|
||||
| [name](variables.tf#L26) | NEG name. | <code>string</code> | ✓ | |
|
||||
| [network](variables.tf#L31) | Name or self link of the VPC used for the NEG. Use the self link for Shared VPC. | <code>string</code> | ✓ | |
|
||||
| [project_id](variables.tf#L36) | NEG project id. | <code>string</code> | ✓ | |
|
||||
| [subnetwork](variables.tf#L41) | VPC subnetwork name or self link. | <code>string</code> | ✓ | |
|
||||
| [zone](variables.tf#L46) | NEG zone. | <code>string</code> | ✓ | |
|
||||
|
||||
## Outputs
|
||||
|
||||
| name | description | sensitive |
|
||||
|---|---|:---:|
|
||||
| [id](outputs.tf#L17) | Network endpoint group ID. | |
|
||||
| [self_lnk](outputs.tf#L22) | Network endpoint group self link. | |
|
||||
| [size](outputs.tf#L27) | Size of the network endpoint group. | |
|
||||
|
||||
<!-- END TFDOC -->
|
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
resource "google_compute_network_endpoint_group" "group" {
|
||||
project = var.project_id
|
||||
name = var.name
|
||||
network = var.network
|
||||
subnetwork = var.subnetwork
|
||||
zone = var.zone
|
||||
}
|
||||
|
||||
resource "google_compute_network_endpoint" "endpoint" {
|
||||
for_each = { for endpoint in var.endpoints : endpoint.instance => endpoint }
|
||||
project = var.project_id
|
||||
network_endpoint_group = google_compute_network_endpoint_group.group.name
|
||||
instance = each.value.instance
|
||||
port = each.value.port
|
||||
ip_address = each.value.ip_address
|
||||
zone = var.zone
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
output "id" {
|
||||
description = "Network endpoint group ID."
|
||||
value = google_compute_network_endpoint_group.group.name
|
||||
}
|
||||
|
||||
output "self_lnk" {
|
||||
description = "Network endpoint group self link."
|
||||
value = google_compute_network_endpoint_group.group.self_link
|
||||
}
|
||||
|
||||
output "size" {
|
||||
description = "Size of the network endpoint group."
|
||||
value = google_compute_network_endpoint_group.group.size
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* 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 "endpoints" {
|
||||
description = "List of (instance, port, address) of the NEG."
|
||||
type = list(object({
|
||||
instance = string
|
||||
port = number
|
||||
ip_address = string
|
||||
}))
|
||||
}
|
||||
|
||||
variable "name" {
|
||||
description = "NEG name."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "network" {
|
||||
description = "Name or self link of the VPC used for the NEG. Use the self link for Shared VPC."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "project_id" {
|
||||
description = "NEG project id."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "subnetwork" {
|
||||
description = "VPC subnetwork name or self link."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "zone" {
|
||||
description = "NEG zone."
|
||||
type = string
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
# 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
|
||||
#
|
||||
# https://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.
|
||||
|
||||
terraform {
|
||||
required_version = ">= 1.4.4"
|
||||
required_providers {
|
||||
google = {
|
||||
source = "hashicorp/google"
|
||||
version = ">= 4.82.0" # tftest
|
||||
}
|
||||
google-beta = {
|
||||
source = "hashicorp/google-beta"
|
||||
version = ">= 4.82.0" # tftest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
# AlloyDB cluster and instance with read replicas
|
||||
|
||||
This module manages the creation of AlloyDB cluster and configuration with/without automated backup policy, Primary node instance and Read Node Pools.
|
||||
|
||||
|
||||
## Simple example
|
||||
|
||||
This example shows how to create Alloydb cluster and instance with multiple read pools in GCP project.
|
||||
|
||||
```hcl
|
||||
module "alloydb" {
|
||||
source = "./fabric/modules/alloydb-instance"
|
||||
project_id = "myproject"
|
||||
cluster_id = "alloydb-cluster-all"
|
||||
location = "europe-west2"
|
||||
labels = {}
|
||||
display_name = ""
|
||||
initial_user = {
|
||||
user = "alloydb-cluster-full",
|
||||
password = "alloydb-cluster-password"
|
||||
}
|
||||
network_self_link = "projects/myproject/global/networks/default"
|
||||
|
||||
automated_backup_policy = null
|
||||
|
||||
primary_instance_config = {
|
||||
instance_id = "primary-instance-1",
|
||||
instance_type = "PRIMARY",
|
||||
machine_cpu_count = 2,
|
||||
database_flags = {},
|
||||
display_name = "alloydb-primary-instance"
|
||||
}
|
||||
read_pool_instance = [
|
||||
{
|
||||
instance_id = "read-instance-1",
|
||||
display_name = "read-instance-1",
|
||||
instance_type = "READ_POOL",
|
||||
node_count = 1,
|
||||
database_flags = {},
|
||||
machine_cpu_count = 1
|
||||
},
|
||||
{
|
||||
instance_id = "read-instance-2",
|
||||
display_name = "read-instance-2",
|
||||
instance_type = "READ_POOL",
|
||||
node_count = 1,
|
||||
database_flags = {},
|
||||
machine_cpu_count = 1
|
||||
}
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
# tftest modules=1 resources=7
|
||||
```
|
||||
## TODO
|
||||
- [ ] Add IAM support
|
||||
- [ ] support password in output
|
||||
<!-- BEGIN TFDOC -->
|
||||
|
||||
## Variables
|
||||
|
||||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [cluster_id](variables.tf#L35) | The ID of the alloydb cluster. | <code>string</code> | ✓ | |
|
||||
| [network_self_link](variables.tf#L83) | Network ID where the AlloyDb cluster will be deployed. | <code>string</code> | ✓ | |
|
||||
| [primary_instance_config](variables.tf#L88) | Primary cluster configuration that supports read and write operations. | <code title="object({ instance_id = string, display_name = optional(string), database_flags = optional(map(string)) labels = optional(map(string)) annotations = optional(map(string)) gce_zone = optional(string) availability_type = optional(string) machine_cpu_count = optional(number, 2), })">object({…})</code> | ✓ | |
|
||||
| [project_id](variables.tf#L110) | The ID of the project in which to provision resources. | <code>string</code> | ✓ | |
|
||||
| [automated_backup_policy](variables.tf#L17) | The automated backup policy for this cluster. | <code title="object({ location = optional(string) backup_window = optional(string) enabled = optional(bool) weekly_schedule = optional(object({ days_of_week = optional(list(string)) start_times = list(string) })), quantity_based_retention_count = optional(number) time_based_retention_count = optional(string) labels = optional(map(string)) backup_encryption_key_name = optional(string) })">object({…})</code> | | <code>null</code> |
|
||||
| [display_name](variables.tf#L44) | Human readable display name for the Alloy DB Cluster. | <code>string</code> | | <code>null</code> |
|
||||
| [encryption_key_name](variables.tf#L50) | The fully-qualified resource name of the KMS key for cluster encryption. | <code>string</code> | | <code>null</code> |
|
||||
| [initial_user](variables.tf#L56) | Alloy DB Cluster Initial User Credentials. | <code title="object({ user = optional(string), password = string })">object({…})</code> | | <code>null</code> |
|
||||
| [labels](variables.tf#L65) | User-defined labels for the alloydb cluster. | <code>map(string)</code> | | <code>{}</code> |
|
||||
| [location](variables.tf#L71) | Location where AlloyDb cluster will be deployed. | <code>string</code> | | <code>"europe-west2"</code> |
|
||||
| [network_name](variables.tf#L77) | The network name of the project in which to provision resources. | <code>string</code> | | <code>"multiple-readpool"</code> |
|
||||
| [read_pool_instance](variables.tf#L115) | List of Read Pool Instances to be created. | <code title="list(object({ instance_id = string display_name = string node_count = optional(number, 1) database_flags = optional(map(string)) availability_type = optional(string) gce_zone = optional(string) machine_cpu_count = optional(number, 2) }))">list(object({…}))</code> | | <code>[]</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
| name | description | sensitive |
|
||||
|---|---|:---:|
|
||||
| [cluster](outputs.tf#L17) | Cluster created. | ✓ |
|
||||
| [cluster_id](outputs.tf#L23) | ID of the Alloy DB Cluster created. | |
|
||||
| [primary_instance](outputs.tf#L28) | Primary instance created. | |
|
||||
| [primary_instance_id](outputs.tf#L33) | ID of the primary instance created. | |
|
||||
| [read_pool_instance_ids](outputs.tf#L38) | IDs of the read instances created. | |
|
||||
|
||||
<!-- END TFDOC -->
|
|
@ -0,0 +1,160 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
locals {
|
||||
quantity_based_retention_count = (
|
||||
var.automated_backup_policy != null ? (var.automated_backup_policy.quantity_based_retention_count != null ? [var.automated_backup_policy.quantity_based_retention_count] : []) : []
|
||||
)
|
||||
read_pool_instance = (
|
||||
var.read_pool_instance != null ?
|
||||
{ for read_pool_instances in var.read_pool_instance : read_pool_instances.instance_id => read_pool_instances } : {}
|
||||
)
|
||||
time_based_retention_count = (
|
||||
var.automated_backup_policy != null ? (var.automated_backup_policy.time_based_retention_count != null ? [var.automated_backup_policy.time_based_retention_count] : []) : []
|
||||
)
|
||||
}
|
||||
|
||||
resource "google_alloydb_cluster" "default" {
|
||||
cluster_id = var.cluster_id
|
||||
location = var.location
|
||||
network = var.network_self_link
|
||||
display_name = var.display_name
|
||||
project = var.project_id
|
||||
labels = var.labels
|
||||
|
||||
dynamic "automated_backup_policy" {
|
||||
for_each = var.automated_backup_policy == null ? [] : [""]
|
||||
content {
|
||||
location = var.automated_backup_policy.location
|
||||
backup_window = var.automated_backup_policy.backup_window
|
||||
enabled = var.automated_backup_policy.enabled
|
||||
labels = var.automated_backup_policy.labels
|
||||
|
||||
|
||||
weekly_schedule {
|
||||
days_of_week = automated_backup_policy.value.weekly_schedule.days_of_week
|
||||
dynamic "start_times" {
|
||||
for_each = { for i, time in automated_backup_policy.value.weekly_schedule.start_times : i => {
|
||||
hours = tonumber(split(":", time)[0])
|
||||
minutes = tonumber(split(":", time)[1])
|
||||
seconds = tonumber(split(":", time)[2])
|
||||
nanos = tonumber(split(":", time)[3])
|
||||
}
|
||||
}
|
||||
content {
|
||||
hours = start_times.value.hours
|
||||
minutes = start_times.value.minutes
|
||||
seconds = start_times.value.seconds
|
||||
nanos = start_times.value.nanos
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "quantity_based_retention" {
|
||||
for_each = local.quantity_based_retention_count
|
||||
content {
|
||||
count = quantity_based_retention.value
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "time_based_retention" {
|
||||
for_each = local.time_based_retention_count
|
||||
content {
|
||||
retention_period = time_based_retention.value
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "encryption_config" {
|
||||
for_each = automated_backup_policy.value.backup_encryption_key_name == null ? [] : ["encryption_config"]
|
||||
content {
|
||||
kms_key_name = automated_backup_policy.value.backup_encryption_key_name
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dynamic "initial_user" {
|
||||
for_each = var.initial_user == null ? [] : ["initial_user"]
|
||||
content {
|
||||
user = var.initial_user.user
|
||||
password = var.initial_user.password
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "encryption_config" {
|
||||
for_each = var.encryption_key_name == null ? [] : ["encryption_config"]
|
||||
content {
|
||||
kms_key_name = var.encryption_key_name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_alloydb_instance" "primary" {
|
||||
cluster = google_alloydb_cluster.default.name
|
||||
instance_id = var.primary_instance_config.instance_id
|
||||
instance_type = "PRIMARY"
|
||||
display_name = var.primary_instance_config.display_name
|
||||
database_flags = var.primary_instance_config.database_flags
|
||||
labels = var.primary_instance_config.labels
|
||||
annotations = var.primary_instance_config.annotations
|
||||
gce_zone = var.primary_instance_config.availability_type == "ZONAL" ? var.primary_instance_config.gce_zone : null
|
||||
availability_type = var.primary_instance_config.availability_type
|
||||
|
||||
machine_config {
|
||||
cpu_count = var.primary_instance_config.machine_cpu_count
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
resource "google_alloydb_instance" "read_pool" {
|
||||
for_each = local.read_pool_instance
|
||||
cluster = google_alloydb_cluster.default.name
|
||||
instance_id = each.key
|
||||
instance_type = "READ_POOL"
|
||||
availability_type = each.value.availability_type
|
||||
gce_zone = each.value.availability_type == "ZONAL" ? each.value.availability_type.gce_zone : null
|
||||
|
||||
read_pool_config {
|
||||
node_count = each.value.node_count
|
||||
}
|
||||
|
||||
database_flags = each.value.database_flags
|
||||
machine_config {
|
||||
cpu_count = each.value.machine_cpu_count
|
||||
}
|
||||
|
||||
depends_on = [google_alloydb_instance.primary, google_compute_network.default, google_compute_global_address.private_ip_alloc, google_service_networking_connection.vpc_connection]
|
||||
}
|
||||
|
||||
resource "google_compute_network" "default" {
|
||||
name = var.network_name
|
||||
}
|
||||
|
||||
resource "google_compute_global_address" "private_ip_alloc" {
|
||||
name = "adb-all"
|
||||
address_type = "INTERNAL"
|
||||
purpose = "VPC_PEERING"
|
||||
prefix_length = 16
|
||||
network = google_compute_network.default.id
|
||||
}
|
||||
|
||||
resource "google_service_networking_connection" "vpc_connection" {
|
||||
network = google_compute_network.default.id
|
||||
service = "servicenetworking.googleapis.com"
|
||||
reserved_peering_ranges = [google_compute_global_address.private_ip_alloc.name]
|
||||
}
|
|
@ -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.
|
||||
*/
|
||||
|
||||
output "cluster" {
|
||||
description = "Cluster created."
|
||||
value = resource.google_alloydb_cluster.default.display_name
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
output "cluster_id" {
|
||||
description = "ID of the Alloy DB Cluster created."
|
||||
value = google_alloydb_cluster.default.cluster_id
|
||||
}
|
||||
|
||||
output "primary_instance" {
|
||||
description = "Primary instance created."
|
||||
value = resource.google_alloydb_instance.primary.display_name
|
||||
}
|
||||
|
||||
output "primary_instance_id" {
|
||||
description = "ID of the primary instance created."
|
||||
value = google_alloydb_instance.primary.instance_id
|
||||
}
|
||||
|
||||
output "read_pool_instance_ids" {
|
||||
description = "IDs of the read instances created."
|
||||
value = [
|
||||
for rd, details in google_alloydb_instance.read_pool : details.instance_id
|
||||
]
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
/**
|
||||
* 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 "automated_backup_policy" {
|
||||
description = "The automated backup policy for this cluster."
|
||||
type = object({
|
||||
location = optional(string)
|
||||
backup_window = optional(string)
|
||||
enabled = optional(bool)
|
||||
weekly_schedule = optional(object({
|
||||
days_of_week = optional(list(string))
|
||||
start_times = list(string)
|
||||
})),
|
||||
quantity_based_retention_count = optional(number)
|
||||
time_based_retention_count = optional(string)
|
||||
labels = optional(map(string))
|
||||
backup_encryption_key_name = optional(string)
|
||||
})
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "cluster_id" {
|
||||
description = "The ID of the alloydb cluster."
|
||||
type = string
|
||||
validation {
|
||||
condition = can(regex("^[a-z0-9-]+$", var.cluster_id))
|
||||
error_message = "ERROR: Cluster ID must contain only Letters(lowercase), number, and hyphen."
|
||||
}
|
||||
}
|
||||
|
||||
variable "display_name" {
|
||||
description = "Human readable display name for the Alloy DB Cluster."
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "encryption_key_name" {
|
||||
description = "The fully-qualified resource name of the KMS key for cluster encryption."
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "initial_user" {
|
||||
description = "Alloy DB Cluster Initial User Credentials."
|
||||
type = object({
|
||||
user = optional(string),
|
||||
password = string
|
||||
})
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "labels" {
|
||||
description = "User-defined labels for the alloydb cluster."
|
||||
type = map(string)
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "location" {
|
||||
description = "Location where AlloyDb cluster will be deployed."
|
||||
type = string
|
||||
default = "europe-west2"
|
||||
}
|
||||
|
||||
variable "network_name" {
|
||||
description = "The network name of the project in which to provision resources."
|
||||
type = string
|
||||
default = "multiple-readpool"
|
||||
}
|
||||
|
||||
variable "network_self_link" {
|
||||
description = "Network ID where the AlloyDb cluster will be deployed."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "primary_instance_config" {
|
||||
description = "Primary cluster configuration that supports read and write operations."
|
||||
type = object({
|
||||
instance_id = string,
|
||||
display_name = optional(string),
|
||||
database_flags = optional(map(string))
|
||||
labels = optional(map(string))
|
||||
annotations = optional(map(string))
|
||||
gce_zone = optional(string)
|
||||
availability_type = optional(string)
|
||||
machine_cpu_count = optional(number, 2),
|
||||
})
|
||||
validation {
|
||||
condition = can(regex("^(2|4|8|16|32|64)$", var.primary_instance_config.machine_cpu_count))
|
||||
error_message = "cpu count must be one of [2 4 8 16 32 64]."
|
||||
}
|
||||
validation {
|
||||
condition = can(regex("^[a-z]([a-z0-9-]{0,61}[a-z0-9])?$", var.primary_instance_config.instance_id))
|
||||
error_message = "Primary Instance ID should satisfy the following pattern ^[a-z]([a-z0-9-]{0,61}[a-z0-9])?$."
|
||||
}
|
||||
}
|
||||
|
||||
variable "project_id" {
|
||||
description = "The ID of the project in which to provision resources."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "read_pool_instance" {
|
||||
description = "List of Read Pool Instances to be created."
|
||||
type = list(object({
|
||||
instance_id = string
|
||||
display_name = string
|
||||
node_count = optional(number, 1)
|
||||
database_flags = optional(map(string))
|
||||
availability_type = optional(string)
|
||||
gce_zone = optional(string)
|
||||
machine_cpu_count = optional(number, 2)
|
||||
}))
|
||||
default = []
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
# 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
|
||||
#
|
||||
# https://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.
|
||||
|
||||
terraform {
|
||||
required_version = ">= 1.4.4"
|
||||
required_providers {
|
||||
google = {
|
||||
source = "hashicorp/google"
|
||||
version = ">= 4.82.0" # tftest
|
||||
}
|
||||
google-beta = {
|
||||
source = "hashicorp/google-beta"
|
||||
version = ">= 4.82.0" # tftest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
# API Gateway
|
||||
This module allows creating an API with its associated API config and API gateway. It also allows you grant IAM roles on the created resources.
|
||||
|
||||
# Examples
|
||||
|
||||
## Basic example
|
||||
```hcl
|
||||
module "gateway" {
|
||||
source = "./fabric/modules/api-gateway"
|
||||
project_id = "my-project"
|
||||
api_id = "api"
|
||||
region = "europe-west1"
|
||||
spec = <<EOT
|
||||
# The OpenAPI spec contents
|
||||
# ...
|
||||
EOT
|
||||
}
|
||||
# tftest modules=1 resources=4 inventory=basic.yaml
|
||||
```
|
||||
|
||||
## Use existing service account
|
||||
```hcl
|
||||
module "gateway" {
|
||||
source = "./fabric/modules/api-gateway"
|
||||
project_id = "my-project"
|
||||
api_id = "api"
|
||||
region = "europe-west1"
|
||||
service_account_email = "sa@my-project.iam.gserviceaccount.com"
|
||||
iam = {
|
||||
"roles/apigateway.admin" = ["user:user@example.com"]
|
||||
}
|
||||
spec = <<EOT
|
||||
# The OpenAPI spec contents
|
||||
# ...
|
||||
EOT
|
||||
}
|
||||
# tftest modules=1 resources=7 inventory=existing-sa.yaml
|
||||
```
|
||||
|
||||
## Create service account
|
||||
```hcl
|
||||
module "gateway" {
|
||||
source = "./fabric/modules/api-gateway"
|
||||
project_id = "my-project"
|
||||
api_id = "api"
|
||||
region = "europe-west1"
|
||||
service_account_create = true
|
||||
iam = {
|
||||
"roles/apigateway.admin" = ["user:mirene@google.com"]
|
||||
"roles/apigateway.viewer" = ["user:mirene@google.com"]
|
||||
}
|
||||
spec = <<EOT
|
||||
# The OpenAPI spec contents
|
||||
# ...
|
||||
EOT
|
||||
}
|
||||
# tftest modules=1 resources=11 inventory=create-sa.yaml
|
||||
```
|
||||
<!-- BEGIN TFDOC -->
|
||||
|
||||
## Variables
|
||||
|
||||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [api_id](variables.tf#L17) | API identifier. | <code>string</code> | ✓ | |
|
||||
| [project_id](variables.tf#L34) | Project identifier. | <code>string</code> | ✓ | |
|
||||
| [region](variables.tf#L39) | Region. | <code>string</code> | ✓ | |
|
||||
| [spec](variables.tf#L56) | String with the contents of the OpenAPI spec. | <code>string</code> | ✓ | |
|
||||
| [iam](variables.tf#L22) | IAM bindings for the API in {ROLE => [MEMBERS]} format. | <code>map(list(string))</code> | | <code>null</code> |
|
||||
| [labels](variables.tf#L28) | Map of labels. | <code>map(string)</code> | | <code>null</code> |
|
||||
| [service_account_create](variables.tf#L44) | Flag indicating whether a service account needs to be created. | <code>bool</code> | | <code>false</code> |
|
||||
| [service_account_email](variables.tf#L50) | Service account for creating API configs. | <code>string</code> | | <code>null</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
| name | description | sensitive |
|
||||
|---|---|:---:|
|
||||
| [api](outputs.tf#L17) | API. | |
|
||||
| [api_config](outputs.tf#L28) | API configs. | |
|
||||
| [api_config_id](outputs.tf#L39) | The identifiers of the API configs. | |
|
||||
| [api_id](outputs.tf#L50) | API identifier. | |
|
||||
| [default_hostname](outputs.tf#L61) | The default host names of the API gateway. | |
|
||||
| [gateway](outputs.tf#L72) | API gateways. | |
|
||||
| [gateway_id](outputs.tf#L83) | The identifiers of the API gateways. | |
|
||||
| [service_account](outputs.tf#L94) | Service account resource. | |
|
||||
| [service_account_email](outputs.tf#L99) | The service account for creating API configs. | |
|
||||
| [service_account_iam_email](outputs.tf#L104) | The service account for creating API configs. | |
|
||||
|
||||
<!-- END TFDOC -->
|
|
@ -0,0 +1,115 @@
|
|||
/**
|
||||
* 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 {
|
||||
service_account_email = (
|
||||
var.service_account_create
|
||||
? (
|
||||
length(google_service_account.service_account) > 0
|
||||
? google_service_account.service_account[0].email
|
||||
: null
|
||||
)
|
||||
: var.service_account_email
|
||||
)
|
||||
}
|
||||
|
||||
resource "google_api_gateway_api" "api" {
|
||||
provider = google-beta
|
||||
project = var.project_id
|
||||
api_id = var.api_id
|
||||
display_name = var.api_id
|
||||
labels = var.labels
|
||||
}
|
||||
|
||||
resource "google_service_account" "service_account" {
|
||||
count = var.service_account_create ? 1 : 0
|
||||
project = var.project_id
|
||||
account_id = "sa-api-cfg-${google_api_gateway_api.api.api_id}"
|
||||
display_name = "Service account to create API configs for ${google_api_gateway_api.api.api_id} API"
|
||||
}
|
||||
|
||||
resource "google_api_gateway_api_config" "api_config" {
|
||||
provider = google-beta
|
||||
project = google_api_gateway_api.api.project
|
||||
api = google_api_gateway_api.api.api_id
|
||||
api_config_id = "api-cfg-${google_api_gateway_api.api.api_id}-${md5(var.spec)}"
|
||||
display_name = "api-cfg-${google_api_gateway_api.api.api_id}-${md5(var.spec)}"
|
||||
openapi_documents {
|
||||
document {
|
||||
path = "spec.yaml"
|
||||
contents = base64encode(var.spec)
|
||||
}
|
||||
}
|
||||
dynamic "gateway_config" {
|
||||
for_each = local.service_account_email == null ? [] : [""]
|
||||
content {
|
||||
backend_config {
|
||||
google_service_account = local.service_account_email
|
||||
}
|
||||
}
|
||||
}
|
||||
lifecycle {
|
||||
create_before_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_api_gateway_gateway" "gateway" {
|
||||
provider = google-beta
|
||||
project = google_api_gateway_api_config.api_config.project
|
||||
api_config = google_api_gateway_api_config.api_config.id
|
||||
gateway_id = "gw-${google_api_gateway_api.api.api_id}"
|
||||
display_name = "gw-${google_api_gateway_api.api.api_id}"
|
||||
region = var.region
|
||||
lifecycle {
|
||||
create_before_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_project_service" "service" {
|
||||
project = google_api_gateway_gateway.gateway.project
|
||||
service = google_api_gateway_api.api.managed_service
|
||||
disable_on_destroy = true
|
||||
disable_dependent_services = true
|
||||
}
|
||||
|
||||
resource "google_api_gateway_api_iam_binding" "api_iam_bindings" {
|
||||
for_each = coalesce(var.iam, {})
|
||||
provider = google-beta
|
||||
project = google_api_gateway_api.api.project
|
||||
api = google_api_gateway_api.api.api_id
|
||||
role = each.key
|
||||
members = each.value
|
||||
}
|
||||
|
||||
resource "google_api_gateway_api_config_iam_binding" "api_config_iam_bindings" {
|
||||
for_each = coalesce(var.iam, {})
|
||||
provider = google-beta
|
||||
project = google_api_gateway_api_config.api_config.project
|
||||
api = google_api_gateway_api.api.api_id
|
||||
api_config = google_api_gateway_api_config.api_config.api_config_id
|
||||
role = each.key
|
||||
members = each.value
|
||||
}
|
||||
|
||||
resource "google_api_gateway_gateway_iam_binding" "gateway_iam_bindings" {
|
||||
for_each = coalesce(var.iam, {})
|
||||
provider = google-beta
|
||||
project = google_api_gateway_gateway.gateway.project
|
||||
gateway = google_api_gateway_gateway.gateway.gateway_id
|
||||
region = var.region
|
||||
role = each.key
|
||||
members = each.value
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
output "api" {
|
||||
description = "API."
|
||||
value = google_api_gateway_api.api
|
||||
depends_on = [
|
||||
google_project_service.service,
|
||||
google_api_gateway_api_iam_binding.api_iam_bindings,
|
||||
google_api_gateway_api_config_iam_binding.api_config_iam_bindings,
|
||||
google_api_gateway_gateway_iam_binding.gateway_iam_bindings
|
||||
]
|
||||
}
|
||||
|
||||
output "api_config" {
|
||||
description = "API configs."
|
||||
value = google_api_gateway_api_config.api_config
|
||||
depends_on = [
|
||||
google_project_service.service,
|
||||
google_api_gateway_api_iam_binding.api_iam_bindings,
|
||||
google_api_gateway_api_config_iam_binding.api_config_iam_bindings,
|
||||
google_api_gateway_gateway_iam_binding.gateway_iam_bindings
|
||||
]
|
||||
}
|
||||
|
||||
output "api_config_id" {
|
||||
description = "The identifiers of the API configs."
|
||||
value = google_api_gateway_api_config.api_config.api_config_id
|
||||
depends_on = [
|
||||
google_project_service.service,
|
||||
google_api_gateway_api_iam_binding.api_iam_bindings,
|
||||
google_api_gateway_api_config_iam_binding.api_config_iam_bindings,
|
||||
google_api_gateway_gateway_iam_binding.gateway_iam_bindings
|
||||
]
|
||||
}
|
||||
|
||||
output "api_id" {
|
||||
description = "API identifier."
|
||||
value = google_api_gateway_api.api.api_id
|
||||
depends_on = [
|
||||
google_project_service.service,
|
||||
google_api_gateway_api_iam_binding.api_iam_bindings,
|
||||
google_api_gateway_api_config_iam_binding.api_config_iam_bindings,
|
||||
google_api_gateway_gateway_iam_binding.gateway_iam_bindings
|
||||
]
|
||||
}
|
||||
|
||||
output "default_hostname" {
|
||||
description = "The default host names of the API gateway."
|
||||
value = google_api_gateway_gateway.gateway.default_hostname
|
||||
depends_on = [
|
||||
google_project_service.service,
|
||||
google_api_gateway_api_iam_binding.api_iam_bindings,
|
||||
google_api_gateway_api_config_iam_binding.api_config_iam_bindings,
|
||||
google_api_gateway_gateway_iam_binding.gateway_iam_bindings
|
||||
]
|
||||
}
|
||||
|
||||
output "gateway" {
|
||||
description = "API gateways."
|
||||
value = google_api_gateway_gateway.gateway
|
||||
depends_on = [
|
||||
google_project_service.service,
|
||||
google_api_gateway_api_iam_binding.api_iam_bindings,
|
||||
google_api_gateway_api_config_iam_binding.api_config_iam_bindings,
|
||||
google_api_gateway_gateway_iam_binding.gateway_iam_bindings
|
||||
]
|
||||
}
|
||||
|
||||
output "gateway_id" {
|
||||
description = "The identifiers of the API gateways."
|
||||
value = google_api_gateway_gateway.gateway.gateway_id
|
||||
depends_on = [
|
||||
google_project_service.service,
|
||||
google_api_gateway_api_iam_binding.api_iam_bindings,
|
||||
google_api_gateway_api_config_iam_binding.api_config_iam_bindings,
|
||||
google_api_gateway_gateway_iam_binding.gateway_iam_bindings
|
||||
]
|
||||
}
|
||||
|
||||
output "service_account" {
|
||||
description = "Service account resource."
|
||||
value = try(google_service_account.service_account[0], null)
|
||||
}
|
||||
|
||||
output "service_account_email" {
|
||||
description = "The service account for creating API configs."
|
||||
value = local.service_account_email
|
||||
}
|
||||
|
||||
output "service_account_iam_email" {
|
||||
description = "The service account for creating API configs."
|
||||
value = local.service_account_email == null ? null : "serviceAccount:${local.service_account_email}"
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/**
|
||||
* 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 "api_id" {
|
||||
description = "API identifier."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "iam" {
|
||||
description = "IAM bindings for the API in {ROLE => [MEMBERS]} format."
|
||||
type = map(list(string))
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "labels" {
|
||||
description = "Map of labels."
|
||||
type = map(string)
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "project_id" {
|
||||
description = "Project identifier."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "region" {
|
||||
description = "Region."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "service_account_create" {
|
||||
description = "Flag indicating whether a service account needs to be created."
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "service_account_email" {
|
||||
description = "Service account for creating API configs."
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "spec" {
|
||||
description = "String with the contents of the OpenAPI spec."
|
||||
type = string
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
# 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
|
||||
#
|
||||
# https://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.
|
||||
|
||||
terraform {
|
||||
required_version = ">= 1.4.4"
|
||||
required_providers {
|
||||
google = {
|
||||
source = "hashicorp/google"
|
||||
version = ">= 4.82.0" # tftest
|
||||
}
|
||||
google-beta = {
|
||||
source = "hashicorp/google-beta"
|
||||
version = ">= 4.82.0" # tftest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,340 @@
|
|||
# Apigee
|
||||
|
||||
This module simplifies the creation of a Apigee resources (organization, environment groups, environment group attachments, environments, instances and instance attachments).
|
||||
|
||||
## Examples
|
||||
|
||||
<!-- BEGIN TOC -->
|
||||
- [Examples](#examples)
|
||||
- [Minimal example (CLOUD)](#minimal-example-cloud)
|
||||
- [Minimal example with existing organization (CLOUD)](#minimal-example-with-existing-organization-cloud)
|
||||
- [Disable VPC Peering (CLOUD)](#disable-vpc-peering-cloud)
|
||||
- [All resources (CLOUD)](#all-resources-cloud)
|
||||
- [All resources (HYBRID control plane)](#all-resources-hybrid-control-plane)
|
||||
- [New environment group](#new-environment-group)
|
||||
- [New environment](#new-environment)
|
||||
- [New instance (VPC Peering Provisioning Mode)](#new-instance-vpc-peering-provisioning-mode)
|
||||
- [New instance (Non VPC Peering Provisioning Mode)](#new-instance-non-vpc-peering-provisioning-mode)
|
||||
- [New endpoint attachment](#new-endpoint-attachment)
|
||||
- [Apigee add-ons](#apigee-add-ons)
|
||||
- [Variables](#variables)
|
||||
- [Outputs](#outputs)
|
||||
<!-- END TOC -->
|
||||
|
||||
### Minimal example (CLOUD)
|
||||
|
||||
This example shows how to create to create an Apigee organization and deploy instance in it.
|
||||
|
||||
```hcl
|
||||
module "apigee" {
|
||||
source = "./fabric/modules/apigee"
|
||||
project_id = var.project_id
|
||||
organization = {
|
||||
display_name = "Apigee"
|
||||
billing_type = "PAYG"
|
||||
analytics_region = "europe-west1"
|
||||
authorized_network = var.vpc.id
|
||||
runtime_type = "CLOUD"
|
||||
}
|
||||
envgroups = {
|
||||
prod = ["prod.example.com"]
|
||||
}
|
||||
environments = {
|
||||
apis-prod = {
|
||||
display_name = "APIs prod"
|
||||
description = "APIs Prod"
|
||||
envgroups = ["prod"]
|
||||
}
|
||||
}
|
||||
instances = {
|
||||
europe-west1 = {
|
||||
environments = ["apis-prod"]
|
||||
runtime_ip_cidr_range = "10.32.0.0/22"
|
||||
troubleshooting_ip_cidr_range = "10.64.0.0/28"
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=6 inventory=minimal-cloud.yaml
|
||||
```
|
||||
|
||||
### Minimal example with existing organization (CLOUD)
|
||||
|
||||
This example shows how to create to work with an existing organization in the project. Note that in this case we don't specify the IP ranges for the instance, so it requests and allocates an available /22 and /28 CIDR block from Service Networking to deploy the instance.
|
||||
|
||||
```hcl
|
||||
module "apigee" {
|
||||
source = "./fabric/modules/apigee"
|
||||
project_id = var.project_id
|
||||
envgroups = {
|
||||
prod = ["prod.example.com"]
|
||||
}
|
||||
environments = {
|
||||
apis-prod = {
|
||||
display_name = "APIs prod"
|
||||
envgroups = ["prod"]
|
||||
}
|
||||
}
|
||||
instances = {
|
||||
europe-west1 = {
|
||||
environments = ["apis-prod"]
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=5 inventory=minimal-cloud-no-org.yaml
|
||||
```
|
||||
|
||||
### Disable VPC Peering (CLOUD)
|
||||
|
||||
When a new Apigee organization is created, it is automatically peered to the authorized network. You can prevent this from happening by using the `disable_vpc_peering` key in the `organization` variable, as shown below:
|
||||
|
||||
|
||||
```hcl
|
||||
module "apigee" {
|
||||
source = "./fabric/modules/apigee"
|
||||
project_id = var.project_id
|
||||
organization = {
|
||||
display_name = "Apigee"
|
||||
billing_type = "PAYG"
|
||||
analytics_region = "europe-west1"
|
||||
runtime_type = "CLOUD"
|
||||
disable_vpc_peering = true
|
||||
}
|
||||
envgroups = {
|
||||
prod = ["prod.example.com"]
|
||||
}
|
||||
environments = {
|
||||
apis-prod = {
|
||||
display_name = "APIs prod"
|
||||
envgroups = ["prod"]
|
||||
}
|
||||
}
|
||||
instances = {
|
||||
europe-west1 = {
|
||||
environments = ["apis-prod"]
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=6 inventory=no-peering.yaml
|
||||
```
|
||||
|
||||
|
||||
### All resources (CLOUD)
|
||||
|
||||
```hcl
|
||||
module "apigee" {
|
||||
source = "./fabric/modules/apigee"
|
||||
project_id = "my-project"
|
||||
organization = {
|
||||
display_name = "My Organization"
|
||||
description = "My Organization"
|
||||
authorized_network = "my-vpc"
|
||||
runtime_type = "CLOUD"
|
||||
billing_type = "PAYG"
|
||||
database_encryption_key = "123456789"
|
||||
analytics_region = "europe-west1"
|
||||
}
|
||||
envgroups = {
|
||||
test = ["test.example.com"]
|
||||
prod = ["prod.example.com"]
|
||||
}
|
||||
environments = {
|
||||
apis-test = {
|
||||
display_name = "APIs test"
|
||||
description = "APIs Test"
|
||||
envgroups = ["test"]
|
||||
}
|
||||
apis-prod = {
|
||||
display_name = "APIs prod"
|
||||
description = "APIs prod"
|
||||
envgroups = ["prod"]
|
||||
iam = {
|
||||
"roles/viewer" = ["group:devops@myorg.com"]
|
||||
}
|
||||
}
|
||||
}
|
||||
instances = {
|
||||
europe-west1 = {
|
||||
runtime_ip_cidr_range = "10.0.4.0/22"
|
||||
troubleshooting_ip_cidr_range = "10.1.1.0.0/28"
|
||||
environments = ["apis-test"]
|
||||
}
|
||||
europe-west3 = {
|
||||
runtime_ip_cidr_range = "10.0.8.0/22"
|
||||
troubleshooting_ip_cidr_range = "10.1.16.0/28"
|
||||
environments = ["apis-prod"]
|
||||
enable_nat = true
|
||||
}
|
||||
}
|
||||
endpoint_attachments = {
|
||||
endpoint-backend-1 = {
|
||||
region = "europe-west1"
|
||||
service_attachment = "projects/my-project-1/serviceAttachments/gkebackend1"
|
||||
}
|
||||
endpoint-backend-2 = {
|
||||
region = "europe-west1"
|
||||
service_attachment = "projects/my-project-2/serviceAttachments/gkebackend2"
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=15
|
||||
```
|
||||
|
||||
### All resources (HYBRID control plane)
|
||||
|
||||
```hcl
|
||||
module "apigee" {
|
||||
source = "./fabric/modules/apigee"
|
||||
project_id = "my-project"
|
||||
organization = {
|
||||
display_name = "My Organization"
|
||||
description = "My Organization"
|
||||
runtime_type = "HYBRID"
|
||||
analytics_region = "europe-west1"
|
||||
}
|
||||
envgroups = {
|
||||
test = ["test.example.com"]
|
||||
prod = ["prod.example.com"]
|
||||
}
|
||||
environments = {
|
||||
apis-test = {
|
||||
display_name = "APIs test"
|
||||
description = "APIs Test"
|
||||
envgroups = ["test"]
|
||||
}
|
||||
apis-prod = {
|
||||
display_name = "APIs prod"
|
||||
description = "APIs prod"
|
||||
envgroups = ["prod"]
|
||||
iam = {
|
||||
"roles/viewer" = ["group:devops@myorg.com"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=8
|
||||
```
|
||||
|
||||
### New environment group
|
||||
|
||||
```hcl
|
||||
module "apigee" {
|
||||
source = "./fabric/modules/apigee"
|
||||
project_id = "my-project"
|
||||
envgroups = {
|
||||
test = ["test.example.com"]
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=1
|
||||
```
|
||||
|
||||
### New environment
|
||||
|
||||
```hcl
|
||||
module "apigee" {
|
||||
source = "./fabric/modules/apigee"
|
||||
project_id = "my-project"
|
||||
environments = {
|
||||
apis-test = {
|
||||
display_name = "APIs test"
|
||||
description = "APIs Test"
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=1
|
||||
```
|
||||
|
||||
### New instance (VPC Peering Provisioning Mode)
|
||||
|
||||
```hcl
|
||||
module "apigee" {
|
||||
source = "./fabric/modules/apigee"
|
||||
project_id = "my-project"
|
||||
instances = {
|
||||
europe-west1 = {
|
||||
runtime_ip_cidr_range = "10.0.4.0/22"
|
||||
troubleshooting_ip_cidr_range = "10.1.1.0/28"
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=1
|
||||
```
|
||||
|
||||
### New instance (Non VPC Peering Provisioning Mode)
|
||||
|
||||
```hcl
|
||||
module "apigee" {
|
||||
source = "./fabric/modules/apigee"
|
||||
project_id = "my-project"
|
||||
organization = {
|
||||
display_name = "My Organization"
|
||||
description = "My Organization"
|
||||
runtime_type = "CLOUD"
|
||||
billing_type = "Pay-as-you-go"
|
||||
database_encryption_key = "123456789"
|
||||
analytics_region = "europe-west1"
|
||||
disable_vpc_peering = true
|
||||
}
|
||||
instances = {
|
||||
europe-west1 = {}
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=2
|
||||
```
|
||||
|
||||
### New endpoint attachment
|
||||
|
||||
Endpoint attachments allow to implement [Apigee southbound network patterns](https://cloud.google.com/apigee/docs/api-platform/architecture/southbound-networking-patterns-endpoints#create-the-psc-attachments).
|
||||
|
||||
```hcl
|
||||
module "apigee" {
|
||||
source = "./fabric/modules/apigee"
|
||||
project_id = "my-project"
|
||||
endpoint_attachments = {
|
||||
endpoint-backend-1 = {
|
||||
region = "europe-west1"
|
||||
service_attachment = "projects/my-project-1/serviceAttachments/gkebackend1"
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=1
|
||||
```
|
||||
|
||||
### Apigee add-ons
|
||||
|
||||
```hcl
|
||||
module "apigee" {
|
||||
source = "./fabric/modules/apigee"
|
||||
project_id = "my-project"
|
||||
addons_config = {
|
||||
monetization = true
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=1
|
||||
```
|
||||
<!-- BEGIN TFDOC -->
|
||||
## Variables
|
||||
|
||||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [project_id](variables.tf#L117) | Project ID. | <code>string</code> | ✓ | |
|
||||
| [addons_config](variables.tf#L17) | Addons configuration. | <code title="object({ advanced_api_ops = optional(bool, false) api_security = optional(bool, false) connectors_platform = optional(bool, false) integration = optional(bool, false) monetization = optional(bool, false) })">object({…})</code> | | <code>null</code> |
|
||||
| [endpoint_attachments](variables.tf#L29) | Endpoint attachments. | <code title="map(object({ region = string service_attachment = string }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [envgroups](variables.tf#L39) | Environment groups (NAME => [HOSTNAMES]). | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [environments](variables.tf#L46) | Environments. | <code title="map(object({ display_name = optional(string) description = optional(string, "Terraform-managed") deployment_type = optional(string) api_proxy_type = optional(string) node_config = optional(object({ min_node_count = optional(number) max_node_count = optional(number) })) iam = optional(map(list(string))) envgroups = optional(list(string)) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [instances](variables.tf#L64) | Instances ([REGION] => [INSTANCE]). | <code title="map(object({ name = optional(string) display_name = optional(string) description = optional(string, "Terraform-managed") runtime_ip_cidr_range = optional(string) troubleshooting_ip_cidr_range = optional(string) disk_encryption_key = optional(string) consumer_accept_list = optional(list(string)) enable_nat = optional(bool, false) environments = optional(list(string)) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [organization](variables.tf#L89) | Apigee organization. If set to null the organization must already exist. | <code title="object({ display_name = optional(string) description = optional(string, "Terraform-managed") authorized_network = optional(string) runtime_type = optional(string, "CLOUD") billing_type = optional(string) database_encryption_key = optional(string) analytics_region = optional(string, "europe-west1") retention = optional(string) disable_vpc_peering = optional(bool, false) })">object({…})</code> | | <code>null</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
| name | description | sensitive |
|
||||
|---|---|:---:|
|
||||
| [endpoint_attachment_hosts](outputs.tf#L17) | Endpoint hosts. | |
|
||||
| [envgroups](outputs.tf#L22) | Environment groups. | |
|
||||
| [environments](outputs.tf#L27) | Environment. | |
|
||||
| [instances](outputs.tf#L32) | Instances. | |
|
||||
| [nat_ips](outputs.tf#L37) | NAT IP addresses used in instances. | |
|
||||
| [org_id](outputs.tf#L45) | Organization ID. | |
|
||||
| [org_name](outputs.tf#L50) | Organization name. | |
|
||||
| [organization](outputs.tf#L55) | Organization. | |
|
||||
| [service_attachments](outputs.tf#L60) | Service attachments. | |
|
||||
<!-- END TFDOC -->
|
|
@ -0,0 +1,155 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
locals {
|
||||
org_id = try(google_apigee_organization.organization[0].id, "organizations/${var.project_id}")
|
||||
org_name = try(google_apigee_organization.organization[0].name, var.project_id)
|
||||
}
|
||||
|
||||
resource "google_apigee_organization" "organization" {
|
||||
count = var.organization == null ? 0 : 1
|
||||
analytics_region = var.organization.analytics_region
|
||||
project_id = var.project_id
|
||||
authorized_network = var.organization.authorized_network
|
||||
billing_type = var.organization.billing_type
|
||||
runtime_type = var.organization.runtime_type
|
||||
runtime_database_encryption_key_name = var.organization.database_encryption_key
|
||||
retention = var.organization.retention
|
||||
disable_vpc_peering = var.organization.disable_vpc_peering
|
||||
}
|
||||
|
||||
resource "google_apigee_envgroup" "envgroups" {
|
||||
for_each = var.envgroups
|
||||
name = each.key
|
||||
hostnames = each.value
|
||||
org_id = local.org_id
|
||||
}
|
||||
|
||||
resource "google_apigee_environment" "environments" {
|
||||
for_each = var.environments
|
||||
name = each.key
|
||||
display_name = each.value.display_name
|
||||
description = each.value.description
|
||||
deployment_type = each.value.deployment_type
|
||||
api_proxy_type = each.value.api_proxy_type
|
||||
dynamic "node_config" {
|
||||
for_each = try(each.value.node_config, null) != null ? [""] : []
|
||||
content {
|
||||
min_node_count = each.value.node_config.min_node_count
|
||||
max_node_count = each.value.node_config.max_node_count
|
||||
}
|
||||
}
|
||||
org_id = local.org_id
|
||||
lifecycle {
|
||||
ignore_changes = [
|
||||
node_config["current_aggregate_node_count"]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_apigee_envgroup_attachment" "envgroup_attachments" {
|
||||
for_each = merge(concat([for k1, v1 in var.environments : {
|
||||
for v2 in coalesce(v1.envgroups, []) : "${k1}-${v2}" => {
|
||||
environment = k1
|
||||
envgroup = v2
|
||||
}
|
||||
}])...)
|
||||
envgroup_id = try(google_apigee_envgroup.envgroups[each.value.envgroup].id, each.value.envgroup)
|
||||
environment = google_apigee_environment.environments[each.value.environment].name
|
||||
}
|
||||
|
||||
resource "google_apigee_environment_iam_binding" "binding" {
|
||||
for_each = merge(concat([for k1, v1 in var.environments : {
|
||||
for k2, v2 in coalesce(v1.iam, {}) : "${k1}-${k2}" => {
|
||||
environment = "${k1}"
|
||||
role = k2
|
||||
members = v2
|
||||
}
|
||||
}])...)
|
||||
org_id = local.org_id
|
||||
env_id = google_apigee_environment.environments[each.value.environment].name
|
||||
role = each.value.role
|
||||
members = each.value.members
|
||||
}
|
||||
|
||||
resource "google_apigee_instance" "instances" {
|
||||
for_each = var.instances
|
||||
name = coalesce(each.value.name, "instance-${each.key}")
|
||||
display_name = each.value.display_name
|
||||
description = each.value.description
|
||||
location = each.key
|
||||
org_id = local.org_id
|
||||
ip_range = (
|
||||
compact([each.value.runtime_ip_cidr_range, each.value.troubleshooting_ip_cidr_range]) == []
|
||||
? null
|
||||
: join(",", compact([each.value.runtime_ip_cidr_range, each.value.troubleshooting_ip_cidr_range]))
|
||||
)
|
||||
disk_encryption_key_name = each.value.disk_encryption_key
|
||||
consumer_accept_list = each.value.consumer_accept_list
|
||||
}
|
||||
|
||||
resource "google_apigee_nat_address" "apigee_nat" {
|
||||
for_each = {
|
||||
for k, v in var.instances :
|
||||
k => google_apigee_instance.instances[k].id
|
||||
if v.enable_nat
|
||||
}
|
||||
name = each.key
|
||||
instance_id = each.value
|
||||
}
|
||||
|
||||
resource "google_apigee_instance_attachment" "instance_attachments" {
|
||||
for_each = merge(concat([for k1, v1 in var.instances : {
|
||||
for v2 in coalesce(v1.environments, []) :
|
||||
"${k1}-${v2}" => {
|
||||
instance = k1
|
||||
environment = v2
|
||||
}
|
||||
}])...)
|
||||
instance_id = google_apigee_instance.instances[each.value.instance].id
|
||||
environment = try(google_apigee_environment.environments[each.value.environment].name,
|
||||
"${local.org_id}/environments/${each.value.environment}")
|
||||
}
|
||||
|
||||
resource "google_apigee_endpoint_attachment" "endpoint_attachments" {
|
||||
for_each = var.endpoint_attachments
|
||||
org_id = local.org_id
|
||||
endpoint_attachment_id = each.key
|
||||
location = each.value.region
|
||||
service_attachment = each.value.service_attachment
|
||||
}
|
||||
|
||||
resource "google_apigee_addons_config" "addons_config" {
|
||||
for_each = toset(var.addons_config == null ? [] : [""])
|
||||
org = local.org_name
|
||||
addons_config {
|
||||
advanced_api_ops_config {
|
||||
enabled = var.addons_config.advanced_api_ops
|
||||
}
|
||||
api_security_config {
|
||||
enabled = var.addons_config.api_security
|
||||
}
|
||||
connectors_platform_config {
|
||||
enabled = var.addons_config.connectors_platform
|
||||
}
|
||||
integration_config {
|
||||
enabled = var.addons_config.integration
|
||||
}
|
||||
monetization_config {
|
||||
enabled = var.addons_config.monetization
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* 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 "endpoint_attachment_hosts" {
|
||||
description = "Endpoint hosts."
|
||||
value = { for k, v in google_apigee_endpoint_attachment.endpoint_attachments : k => v.host }
|
||||
}
|
||||
|
||||
output "envgroups" {
|
||||
description = "Environment groups."
|
||||
value = try(google_apigee_envgroup.envgroups, null)
|
||||
}
|
||||
|
||||
output "environments" {
|
||||
description = "Environment."
|
||||
value = try(google_apigee_environment.environments, null)
|
||||
}
|
||||
|
||||
output "instances" {
|
||||
description = "Instances."
|
||||
value = try(google_apigee_instance.instances, null)
|
||||
}
|
||||
|
||||
output "nat_ips" {
|
||||
description = "NAT IP addresses used in instances."
|
||||
value = {
|
||||
for k, v in google_apigee_nat_address.apigee_nat :
|
||||
k => v.ip_address
|
||||
}
|
||||
}
|
||||
|
||||
output "org_id" {
|
||||
description = "Organization ID."
|
||||
value = local.org_id
|
||||
}
|
||||
|
||||
output "org_name" {
|
||||
description = "Organization name."
|
||||
value = try(google_apigee_organization.organization[0].name, var.project_id)
|
||||
}
|
||||
|
||||
output "organization" {
|
||||
description = "Organization."
|
||||
value = try(google_apigee_organization.organization[0], null)
|
||||
}
|
||||
|
||||
output "service_attachments" {
|
||||
description = "Service attachments."
|
||||
value = { for k, v in google_apigee_instance.instances : k => v.service_attachment }
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
/**
|
||||
* 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 "addons_config" {
|
||||
description = "Addons configuration."
|
||||
type = object({
|
||||
advanced_api_ops = optional(bool, false)
|
||||
api_security = optional(bool, false)
|
||||
connectors_platform = optional(bool, false)
|
||||
integration = optional(bool, false)
|
||||
monetization = optional(bool, false)
|
||||
})
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "endpoint_attachments" {
|
||||
description = "Endpoint attachments."
|
||||
type = map(object({
|
||||
region = string
|
||||
service_attachment = string
|
||||
}))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "envgroups" {
|
||||
description = "Environment groups (NAME => [HOSTNAMES])."
|
||||
type = map(list(string))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "environments" {
|
||||
description = "Environments."
|
||||
type = map(object({
|
||||
display_name = optional(string)
|
||||
description = optional(string, "Terraform-managed")
|
||||
deployment_type = optional(string)
|
||||
api_proxy_type = optional(string)
|
||||
node_config = optional(object({
|
||||
min_node_count = optional(number)
|
||||
max_node_count = optional(number)
|
||||
}))
|
||||
iam = optional(map(list(string)))
|
||||
envgroups = optional(list(string))
|
||||
}))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "instances" {
|
||||
description = "Instances ([REGION] => [INSTANCE])."
|
||||
type = map(object({
|
||||
name = optional(string)
|
||||
display_name = optional(string)
|
||||
description = optional(string, "Terraform-managed")
|
||||
runtime_ip_cidr_range = optional(string)
|
||||
troubleshooting_ip_cidr_range = optional(string)
|
||||
disk_encryption_key = optional(string)
|
||||
consumer_accept_list = optional(list(string))
|
||||
enable_nat = optional(bool, false)
|
||||
environments = optional(list(string))
|
||||
}))
|
||||
validation {
|
||||
condition = alltrue([
|
||||
for k, v in var.instances :
|
||||
# has troubleshooting_ip => has runtime_ip
|
||||
v.runtime_ip_cidr_range != null || v.troubleshooting_ip_cidr_range == null
|
||||
])
|
||||
error_message = "Using a troubleshooting range requires specifying a runtime range too."
|
||||
}
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "organization" {
|
||||
description = "Apigee organization. If set to null the organization must already exist."
|
||||
type = object({
|
||||
display_name = optional(string)
|
||||
description = optional(string, "Terraform-managed")
|
||||
authorized_network = optional(string)
|
||||
runtime_type = optional(string, "CLOUD")
|
||||
billing_type = optional(string)
|
||||
database_encryption_key = optional(string)
|
||||
analytics_region = optional(string, "europe-west1")
|
||||
retention = optional(string)
|
||||
disable_vpc_peering = optional(bool, false)
|
||||
})
|
||||
validation {
|
||||
condition = var.organization == null || (
|
||||
try(var.organization.runtime_type, null) == "CLOUD" || !try(var.organization.disable_vpc_peering, false)
|
||||
)
|
||||
error_message = "Disabling the VPC peering can only be done in organization using the CLOUD runtime."
|
||||
}
|
||||
validation {
|
||||
condition = var.organization == null || (
|
||||
try(var.organization.authorized_network, null) == null || !try(var.organization.disable_vpc_peering, false)
|
||||
)
|
||||
error_message = "Disabling the VPC peering is mutually exclusive with authorized_network."
|
||||
}
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "project_id" {
|
||||
description = "Project ID."
|
||||
type = string
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
# 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
|
||||
#
|
||||
# https://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.
|
||||
|
||||
terraform {
|
||||
required_version = ">= 1.4.4"
|
||||
required_providers {
|
||||
google = {
|
||||
source = "hashicorp/google"
|
||||
version = ">= 4.82.0" # tftest
|
||||
}
|
||||
google-beta = {
|
||||
source = "hashicorp/google-beta"
|
||||
version = ">= 4.82.0" # tftest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
# Google Cloud Artifact Registry Module
|
||||
|
||||
This module simplifies the creation of repositories using Google Cloud Artifact Registry.
|
||||
|
||||
<!-- BEGIN TOC -->
|
||||
- [Standard Repository](#standard-repository)
|
||||
- [Remote and Virtual Repositories](#remote-and-virtual-repositories)
|
||||
- [Additional Docker and Maven Options](#additional-docker-and-maven-options)
|
||||
- [Variables](#variables)
|
||||
- [Outputs](#outputs)
|
||||
<!-- END TOC -->
|
||||
|
||||
## Standard Repository
|
||||
|
||||
```hcl
|
||||
module "docker_artifact_registry" {
|
||||
source = "./fabric/modules/artifact-registry"
|
||||
project_id = "myproject"
|
||||
location = "europe-west1"
|
||||
name = "myregistry"
|
||||
iam = {
|
||||
"roles/artifactregistry.admin" = ["group:cicd@example.com"]
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=2
|
||||
```
|
||||
|
||||
## Remote and Virtual Repositories
|
||||
|
||||
```hcl
|
||||
|
||||
module "registry-local" {
|
||||
source = "./fabric/modules/artifact-registry"
|
||||
project_id = var.project_id
|
||||
location = "europe-west1"
|
||||
name = "local"
|
||||
format = { python = {} }
|
||||
}
|
||||
|
||||
module "registry-remote" {
|
||||
source = "./fabric/modules/artifact-registry"
|
||||
project_id = var.project_id
|
||||
location = "europe-west1"
|
||||
name = "remote"
|
||||
format = { python = {} }
|
||||
mode = { remote = true }
|
||||
}
|
||||
|
||||
module "registry-virtual" {
|
||||
source = "./fabric/modules/artifact-registry"
|
||||
project_id = var.project_id
|
||||
location = "europe-west1"
|
||||
name = "virtual"
|
||||
format = { python = {} }
|
||||
mode = {
|
||||
virtual = {
|
||||
remote = {
|
||||
repository = module.registry-remote.id
|
||||
priority = 1
|
||||
}
|
||||
local = {
|
||||
repository = module.registry-local.id
|
||||
priority = 10
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# tftest modules=3 resources=3 inventory=remote-virtual.yaml
|
||||
```
|
||||
|
||||
## Additional Docker and Maven Options
|
||||
|
||||
```hcl
|
||||
|
||||
module "registry-docker" {
|
||||
source = "./fabric/modules/artifact-registry"
|
||||
project_id = var.project_id
|
||||
location = "europe-west1"
|
||||
name = "docker"
|
||||
format = {
|
||||
docker = {
|
||||
immutable_tags = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module "registry-maven" {
|
||||
source = "./fabric/modules/artifact-registry"
|
||||
project_id = var.project_id
|
||||
location = "europe-west1"
|
||||
name = "maven"
|
||||
format = {
|
||||
maven = {
|
||||
allow_snapshot_overwrites = true
|
||||
version_policy = "RELEASE"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# tftest modules=2 resources=2
|
||||
```
|
||||
<!-- BEGIN TFDOC -->
|
||||
## Variables
|
||||
|
||||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [location](variables.tf#L68) | Registry location. Use `gcloud beta artifacts locations list' to get valid values. | <code>string</code> | ✓ | |
|
||||
| [name](variables.tf#L93) | Registry name. | <code>string</code> | ✓ | |
|
||||
| [project_id](variables.tf#L98) | Registry project id. | <code>string</code> | ✓ | |
|
||||
| [description](variables.tf#L17) | An optional description for the repository. | <code>string</code> | | <code>"Terraform-managed registry"</code> |
|
||||
| [encryption_key](variables.tf#L23) | The KMS key name to use for encryption at rest. | <code>string</code> | | <code>null</code> |
|
||||
| [format](variables.tf#L29) | Repository format. | <code title="object({ apt = optional(object({})) docker = optional(object({ immutable_tags = optional(bool) })) kfp = optional(object({})) go = optional(object({})) maven = optional(object({ allow_snapshot_overwrites = optional(bool) version_policy = optional(string) })) npm = optional(object({})) python = optional(object({})) yum = optional(object({})) })">object({…})</code> | | <code>{ docker = {} }</code> |
|
||||
| [iam](variables.tf#L56) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [labels](variables.tf#L62) | Labels to be attached to the registry. | <code>map(string)</code> | | <code>{}</code> |
|
||||
| [mode](variables.tf#L73) | Repository mode. | <code title="object({ standard = optional(bool) remote = optional(bool) virtual = optional(map(object({ repository = string priority = number }))) })">object({…})</code> | | <code>{ standard = true }</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
| name | description | sensitive |
|
||||
|---|---|:---:|
|
||||
| [id](outputs.tf#L17) | Fully qualified repository id. | |
|
||||
| [image_path](outputs.tf#L22) | Repository path for images. | |
|
||||
| [name](outputs.tf#L32) | Repository name. | |
|
||||
<!-- END TFDOC -->
|
|
@ -0,0 +1,115 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
locals {
|
||||
format_string = one([for k, v in var.format : k if v != null])
|
||||
mode_string = one([for k, v in var.mode : k if v != null && v != false])
|
||||
}
|
||||
|
||||
resource "google_artifact_registry_repository" "registry" {
|
||||
project = var.project_id
|
||||
location = var.location
|
||||
description = var.description
|
||||
format = upper(local.format_string)
|
||||
labels = var.labels
|
||||
repository_id = var.name
|
||||
mode = "${upper(local.mode_string)}_REPOSITORY"
|
||||
kms_key_name = var.encryption_key
|
||||
|
||||
dynamic "docker_config" {
|
||||
# TODO: open a bug on the provider for this permadiff
|
||||
for_each = (
|
||||
local.format_string == "docker" && try(var.format.docker.immutable_tags, null) == true
|
||||
? [""]
|
||||
: []
|
||||
)
|
||||
content {
|
||||
immutable_tags = var.format.docker.immutable_tags
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "maven_config" {
|
||||
for_each = local.format_string == "maven" ? [""] : []
|
||||
content {
|
||||
allow_snapshot_overwrites = var.format.maven.allow_snapshot_overwrites
|
||||
version_policy = var.format.maven.version_policy
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "remote_repository_config" {
|
||||
for_each = local.mode_string == "remote" ? [""] : []
|
||||
content {
|
||||
dynamic "docker_repository" {
|
||||
for_each = local.format_string == "docker" ? [""] : []
|
||||
content {
|
||||
public_repository = "DOCKER_HUB"
|
||||
}
|
||||
}
|
||||
dynamic "maven_repository" {
|
||||
for_each = local.format_string == "maven" ? [""] : []
|
||||
content {
|
||||
public_repository = "MAVEN_CENTRAL"
|
||||
}
|
||||
}
|
||||
dynamic "npm_repository" {
|
||||
for_each = local.format_string == "npm" ? [""] : []
|
||||
content {
|
||||
public_repository = "NPMJS"
|
||||
}
|
||||
}
|
||||
dynamic "python_repository" {
|
||||
for_each = local.format_string == "python" ? [""] : []
|
||||
content {
|
||||
public_repository = "PYPI"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "virtual_repository_config" {
|
||||
for_each = local.mode_string == "virtual" ? [""] : []
|
||||
content {
|
||||
dynamic "upstream_policies" {
|
||||
for_each = var.mode.virtual
|
||||
content {
|
||||
id = upstream_policies.key
|
||||
repository = upstream_policies.value.repository
|
||||
priority = upstream_policies.value.priority
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lifecycle {
|
||||
precondition {
|
||||
condition = local.mode_string != "remote" || contains(
|
||||
["docker", "maven", "npm", "python"], local.format_string
|
||||
)
|
||||
error_message = "Invalid format for remote repository."
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
resource "google_artifact_registry_repository_iam_binding" "bindings" {
|
||||
provider = google-beta
|
||||
for_each = var.iam
|
||||
project = var.project_id
|
||||
location = google_artifact_registry_repository.registry.location
|
||||
repository = google_artifact_registry_repository.registry.name
|
||||
role = each.key
|
||||
members = each.value
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
output "id" {
|
||||
description = "Fully qualified repository id."
|
||||
value = google_artifact_registry_repository.registry.id
|
||||
}
|
||||
|
||||
output "image_path" {
|
||||
description = "Repository path for images."
|
||||
value = join("/", [
|
||||
"${var.location}-docker.pkg.dev",
|
||||
var.project_id,
|
||||
var.name
|
||||
])
|
||||
depends_on = [google_artifact_registry_repository.registry]
|
||||
}
|
||||
|
||||
output "name" {
|
||||
description = "Repository name."
|
||||
value = google_artifact_registry_repository.registry.name
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
/**
|
||||
* 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 "description" {
|
||||
description = "An optional description for the repository."
|
||||
type = string
|
||||
default = "Terraform-managed registry"
|
||||
}
|
||||
|
||||
variable "encryption_key" {
|
||||
description = "The KMS key name to use for encryption at rest."
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "format" {
|
||||
description = "Repository format."
|
||||
type = object({
|
||||
apt = optional(object({}))
|
||||
docker = optional(object({
|
||||
immutable_tags = optional(bool)
|
||||
}))
|
||||
kfp = optional(object({}))
|
||||
go = optional(object({}))
|
||||
maven = optional(object({
|
||||
allow_snapshot_overwrites = optional(bool)
|
||||
version_policy = optional(string)
|
||||
}))
|
||||
npm = optional(object({}))
|
||||
python = optional(object({}))
|
||||
yum = optional(object({}))
|
||||
})
|
||||
nullable = false
|
||||
default = { docker = {} }
|
||||
validation {
|
||||
condition = (
|
||||
length([for k, v in var.format : k if v != null]) == 1
|
||||
)
|
||||
error_message = "Multiple or zero formats are not supported."
|
||||
}
|
||||
}
|
||||
|
||||
variable "iam" {
|
||||
description = "IAM bindings in {ROLE => [MEMBERS]} format."
|
||||
type = map(list(string))
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "labels" {
|
||||
description = "Labels to be attached to the registry."
|
||||
type = map(string)
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "location" {
|
||||
description = "Registry location. Use `gcloud beta artifacts locations list' to get valid values."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "mode" {
|
||||
description = "Repository mode."
|
||||
type = object({
|
||||
standard = optional(bool)
|
||||
remote = optional(bool)
|
||||
virtual = optional(map(object({
|
||||
repository = string
|
||||
priority = number
|
||||
})))
|
||||
})
|
||||
nullable = false
|
||||
default = { standard = true }
|
||||
validation {
|
||||
condition = (
|
||||
length([for k, v in var.mode : k if v != null && v != false]) == 1
|
||||
)
|
||||
error_message = "Multiple or zero modes are not supported."
|
||||
}
|
||||
}
|
||||
|
||||
variable "name" {
|
||||
description = "Registry name."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "project_id" {
|
||||
description = "Registry project id."
|
||||
type = string
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
# 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
|
||||
#
|
||||
# https://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.
|
||||
|
||||
terraform {
|
||||
required_version = ">= 1.4.4"
|
||||
required_providers {
|
||||
google = {
|
||||
source = "hashicorp/google"
|
||||
version = ">= 4.82.0" # tftest
|
||||
}
|
||||
google-beta = {
|
||||
source = "hashicorp/google-beta"
|
||||
version = ">= 4.82.0" # tftest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,312 @@
|
|||
# Google Cloud Bigquery Module
|
||||
|
||||
This module allows managing a single BigQuery dataset, including access configuration, tables and views.
|
||||
|
||||
## TODO
|
||||
|
||||
- [ ] check for dynamic values in tables and views
|
||||
- [ ] add support for external tables
|
||||
|
||||
## Examples
|
||||
|
||||
### Simple dataset with access configuration
|
||||
|
||||
Access configuration defaults to using the separate `google_bigquery_dataset_access` resource, so as to leave the default dataset access rules untouched.
|
||||
|
||||
You can choose to manage the `google_bigquery_dataset` access rules instead via the `dataset_access` variable, but be sure to always have at least one `OWNER` access and to avoid duplicating accesses, or `terraform apply` will fail.
|
||||
|
||||
The access variables are split into `access` and `access_identities` variables, so that dynamic values can be passed in for identities (eg a service account email generated by a different module or resource).
|
||||
|
||||
```hcl
|
||||
module "bigquery-dataset" {
|
||||
source = "./fabric/modules/bigquery-dataset"
|
||||
project_id = "my-project"
|
||||
id = "my-dataset"
|
||||
access = {
|
||||
reader-group = { role = "READER", type = "group" }
|
||||
owner = { role = "OWNER", type = "user" }
|
||||
project_owners = { role = "OWNER", type = "special_group" }
|
||||
view_1 = { role = "READER", type = "view" }
|
||||
}
|
||||
access_identities = {
|
||||
reader-group = "playground-test@ludomagno.net"
|
||||
owner = "ludo@ludomagno.net"
|
||||
project_owners = "projectOwners"
|
||||
view_1 = "my-project|my-dataset|my-table"
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=5 inventory=simple.yaml
|
||||
```
|
||||
|
||||
### IAM roles
|
||||
|
||||
Access configuration can also be specified via IAM instead of basic roles via the `iam` variable. When using IAM, basic roles cannot be used via the `access` family variables.
|
||||
|
||||
```hcl
|
||||
module "bigquery-dataset" {
|
||||
source = "./fabric/modules/bigquery-dataset"
|
||||
project_id = "my-project"
|
||||
id = "my-dataset"
|
||||
iam = {
|
||||
"roles/bigquery.dataOwner" = ["user:user1@example.org"]
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=2 inventory=iam.yaml
|
||||
```
|
||||
|
||||
### Authorized Views, Datasets, and Routines
|
||||
|
||||
You can specify authorized [views](https://cloud.google.com/bigquery/docs/authorized-views), [datasets](https://cloud.google.com/bigquery/docs/authorized-datasets?hl=en), and [routines](https://cloud.google.com/bigquery/docs/authorized-routines) via the `authorized_views`, `authorized_datasets` and `authorized_routines` variables, respectively.
|
||||
|
||||
```hcl
|
||||
// Create private BigQuery dataset that will not be publicly accessible, except via the authorized BigQuery resources
|
||||
module "bigquery-dataset-private" {
|
||||
source = "./fabric/modules/bigquery-dataset"
|
||||
project_id = "private_project"
|
||||
id = "private_dataset"
|
||||
authorized_views = [
|
||||
{
|
||||
project_id = "auth_view_project"
|
||||
dataset_id = "auth_view_dataset"
|
||||
table_id = "auth_view"
|
||||
}
|
||||
]
|
||||
authorized_datasets = [
|
||||
{
|
||||
project_id = "auth_dataset_project"
|
||||
dataset_id = "auth_dataset"
|
||||
}
|
||||
]
|
||||
authorized_routines = [
|
||||
{
|
||||
project_id = "auth_routine_project"
|
||||
dataset_id = "auth_routine_dataset"
|
||||
routine_id = "auth_routine"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// Create authorized view in a public dataset
|
||||
module "bigquery-authorized-views-dataset-public" {
|
||||
source = "./fabric/modules/bigquery-dataset"
|
||||
project_id = "auth_view_project"
|
||||
id = "auth_view_dataset"
|
||||
views = {
|
||||
auth_view = {
|
||||
friendly_name = "Public"
|
||||
labels = {}
|
||||
query = "SELECT * FROM `private_project.private_dataset.private_table`"
|
||||
use_legacy_sql = false
|
||||
deletion_protection = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create public authorized dataset
|
||||
module "bigquery-authorized-dataset-public" {
|
||||
source = "./fabric/modules/bigquery-dataset"
|
||||
project_id = "auth_dataset_project"
|
||||
id = "auth_dataset"
|
||||
}
|
||||
|
||||
// Create public authorized routine
|
||||
module "bigquery-authorized-authorized-routine-dataset-public" {
|
||||
source = "./fabric/modules/bigquery-dataset"
|
||||
project_id = "auth_routine_project"
|
||||
id = "auth_routine_dataset"
|
||||
}
|
||||
|
||||
resource "google_bigquery_routine" "public-routine" {
|
||||
dataset_id = module.bigquery-authorized-authorized-routine-dataset-public.dataset_id
|
||||
routine_id = "auth_routine"
|
||||
routine_type = "TABLE_VALUED_FUNCTION"
|
||||
language = "SQL"
|
||||
definition_body = <<-EOS
|
||||
SELECT 1 + value AS value
|
||||
EOS
|
||||
arguments {
|
||||
name = "value"
|
||||
argument_kind = "FIXED_TYPE"
|
||||
data_type = jsonencode({ "typeKind" = "INT64" })
|
||||
}
|
||||
return_table_type = jsonencode({ "columns" = [
|
||||
{ "name" = "value", "type" = { "typeKind" = "INT64" } },
|
||||
] })
|
||||
}
|
||||
# tftest modules=4 resources=9 inventory=authorized_resources.yaml
|
||||
```
|
||||
|
||||
Authorized views can be specified both using the standard `access` options and the `authorized_views` blocks. The example configuration below uses both blocks, and will create a dataset with three authorized views `view_id_1`, `view_id_2`, and `view_id_3`.
|
||||
|
||||
```hcl
|
||||
module "bigquery-dataset" {
|
||||
source = "./fabric/modules/bigquery-dataset"
|
||||
project_id = "my-project"
|
||||
id = "my-dataset"
|
||||
authorized_views = [
|
||||
{
|
||||
project_id = "view_project"
|
||||
dataset_id = "view_dataset"
|
||||
table_id = "view_id_1"
|
||||
},
|
||||
{
|
||||
project_id = "view_project"
|
||||
dataset_id = "view_dataset"
|
||||
table_id = "view_id_2"
|
||||
}
|
||||
]
|
||||
access = {
|
||||
view_2 = { role = "READER", type = "view" }
|
||||
view_3 = { role = "READER", type = "view" }
|
||||
}
|
||||
access_identities = {
|
||||
view_2 = "view_project|view_dataset|view_id_2"
|
||||
view_3 = "view_project|view_dataset|view_id_3"
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=4 inventory=authorized_resources_views.yaml
|
||||
```
|
||||
|
||||
### Dataset options
|
||||
|
||||
Dataset options are set via the `options` variable. all options must be specified, but a `null` value can be set to options that need to use defaults.
|
||||
|
||||
```hcl
|
||||
module "bigquery-dataset" {
|
||||
source = "./fabric/modules/bigquery-dataset"
|
||||
project_id = "my-project"
|
||||
id = "my-dataset"
|
||||
options = {
|
||||
default_table_expiration_ms = 3600000
|
||||
default_partition_expiration_ms = null
|
||||
delete_contents_on_destroy = false
|
||||
max_time_travel_hours = 168
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=1 inventory=options.yaml
|
||||
```
|
||||
|
||||
### Tables and views
|
||||
|
||||
Tables are created via the `tables` variable, or the `view` variable for views. Support for external tables will be added in a future release.
|
||||
|
||||
```hcl
|
||||
locals {
|
||||
countries_schema = jsonencode([
|
||||
{ name = "country", type = "STRING" },
|
||||
{ name = "population", type = "INT64" },
|
||||
])
|
||||
}
|
||||
|
||||
module "bigquery-dataset" {
|
||||
source = "./fabric/modules/bigquery-dataset"
|
||||
project_id = "my-project"
|
||||
id = "my_dataset"
|
||||
tables = {
|
||||
countries = {
|
||||
friendly_name = "Countries"
|
||||
schema = local.countries_schema
|
||||
deletion_protection = true
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=2 inventory=tables.yaml
|
||||
```
|
||||
|
||||
If partitioning is needed, populate the `partitioning` variable using either the `time` or `range` attribute.
|
||||
|
||||
```hcl
|
||||
locals {
|
||||
countries_schema = jsonencode([
|
||||
{ name = "country", type = "STRING" },
|
||||
{ name = "population", type = "INT64" },
|
||||
])
|
||||
}
|
||||
|
||||
module "bigquery-dataset" {
|
||||
source = "./fabric/modules/bigquery-dataset"
|
||||
project_id = "my-project"
|
||||
id = "my-dataset"
|
||||
tables = {
|
||||
table_a = {
|
||||
deletion_protection = true
|
||||
friendly_name = "Table a"
|
||||
schema = local.countries_schema
|
||||
partitioning = {
|
||||
time = { type = "DAY", expiration_ms = null }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=2 inventory=partitioning.yaml
|
||||
```
|
||||
|
||||
To create views use the `view` variable. If you're querying a table created by the same module `terraform apply` will initially fail and eventually succeed once the underlying table has been created. You can probably also use the module's output in the view's query to create a dependency on the table.
|
||||
|
||||
```hcl
|
||||
locals {
|
||||
countries_schema = jsonencode([
|
||||
{ name = "country", type = "STRING" },
|
||||
{ name = "population", type = "INT64" },
|
||||
])
|
||||
}
|
||||
|
||||
module "bigquery-dataset" {
|
||||
source = "./fabric/modules/bigquery-dataset"
|
||||
project_id = "my-project"
|
||||
id = "my_dataset"
|
||||
tables = {
|
||||
countries = {
|
||||
friendly_name = "Countries"
|
||||
schema = local.countries_schema
|
||||
deletion_protection = true
|
||||
}
|
||||
}
|
||||
views = {
|
||||
population = {
|
||||
friendly_name = "Population"
|
||||
query = "SELECT SUM(population) FROM my_dataset.countries"
|
||||
use_legacy_sql = false
|
||||
deletion_protection = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# tftest modules=1 resources=3 inventory=views.yaml
|
||||
```
|
||||
<!-- BEGIN TFDOC -->
|
||||
## Variables
|
||||
|
||||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [id](variables.tf#L98) | Dataset id. | <code>string</code> | ✓ | |
|
||||
| [project_id](variables.tf#L128) | Id of the project where datasets will be created. | <code>string</code> | ✓ | |
|
||||
| [access](variables.tf#L17) | Map of access rules with role and identity type. Keys are arbitrary and must match those in the `access_identities` variable, types are `domain`, `group`, `special_group`, `user`, `view`. | <code title="map(object({ role = string type = string }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [access_identities](variables.tf#L33) | Map of access identities used for basic access roles. View identities have the format 'project_id\|dataset_id\|table_id'. | <code>map(string)</code> | | <code>{}</code> |
|
||||
| [authorized_datasets](variables.tf#L39) | An array of datasets to be authorized on the dataset. | <code title="list(object({ dataset_id = string, project_id = string, }))">list(object({…}))</code> | | <code>[]</code> |
|
||||
| [authorized_routines](variables.tf#L48) | An array of authorized routine to be authorized on the dataset. | <code title="list(object({ project_id = string, dataset_id = string, routine_id = string }))">list(object({…}))</code> | | <code>[]</code> |
|
||||
| [authorized_views](variables.tf#L58) | An array of views to be authorized on the dataset. | <code title="list(object({ dataset_id = string, project_id = string, table_id = string # this is the view id, but we keep table_id to stay consistent as the resource }))">list(object({…}))</code> | | <code>[]</code> |
|
||||
| [dataset_access](variables.tf#L68) | Set access in the dataset resource instead of using separate resources. | <code>bool</code> | | <code>false</code> |
|
||||
| [description](variables.tf#L74) | Optional description. | <code>string</code> | | <code>"Terraform managed."</code> |
|
||||
| [encryption_key](variables.tf#L80) | Self link of the KMS key that will be used to protect destination table. | <code>string</code> | | <code>null</code> |
|
||||
| [friendly_name](variables.tf#L86) | Dataset friendly name. | <code>string</code> | | <code>null</code> |
|
||||
| [iam](variables.tf#L92) | IAM bindings in {ROLE => [MEMBERS]} format. Mutually exclusive with the access_* variables used for basic roles. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [labels](variables.tf#L103) | Dataset labels. | <code>map(string)</code> | | <code>{}</code> |
|
||||
| [location](variables.tf#L109) | Dataset location. | <code>string</code> | | <code>"EU"</code> |
|
||||
| [options](variables.tf#L115) | Dataset options. | <code title="object({ default_collation = optional(string) default_table_expiration_ms = optional(number) default_partition_expiration_ms = optional(number) delete_contents_on_destroy = optional(bool, false) is_case_insensitive = optional(bool) max_time_travel_hours = optional(number, 168) })">object({…})</code> | | <code>{}</code> |
|
||||
| [tables](variables.tf#L133) | Table definitions. Options and partitioning default to null. Partitioning can only use `range` or `time`, set the unused one to null. | <code title="map(object({ deletion_protection = optional(bool) description = optional(string, "Terraform managed.") friendly_name = optional(string) labels = optional(map(string), {}) schema = optional(string) options = optional(object({ clustering = optional(list(string)) encryption_key = optional(string) expiration_time = optional(number) }), {}) partitioning = optional(object({ field = optional(string) range = optional(object({ end = number interval = number start = number })) time = optional(object({ expiration_ms = number type = string })) })) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [views](variables.tf#L162) | View definitions. | <code title="map(object({ query = string deletion_protection = optional(bool) description = optional(string, "Terraform managed.") friendly_name = optional(string) labels = optional(map(string), {}) use_legacy_sql = optional(bool) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
| name | description | sensitive |
|
||||
|---|---|:---:|
|
||||
| [dataset](outputs.tf#L17) | Dataset resource. | |
|
||||
| [dataset_id](outputs.tf#L22) | Dataset id. | |
|
||||
| [id](outputs.tf#L36) | Fully qualified dataset id. | |
|
||||
| [self_link](outputs.tf#L50) | Dataset self link. | |
|
||||
| [table_ids](outputs.tf#L64) | Map of fully qualified table ids keyed by table ids. | |
|
||||
| [tables](outputs.tf#L69) | Table resources. | |
|
||||
| [view_ids](outputs.tf#L74) | Map of fully qualified view ids keyed by view ids. | |
|
||||
| [views](outputs.tf#L79) | View resources. | |
|
||||
<!-- END TFDOC -->
|
|
@ -0,0 +1,268 @@
|
|||
/**
|
||||
* 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 {
|
||||
access_domain = { for k, v in var.access : k => v if v.type == "domain" }
|
||||
access_group = { for k, v in var.access : k => v if v.type == "group" }
|
||||
access_special = { for k, v in var.access : k => v if v.type == "special_group" }
|
||||
access_user = { for k, v in var.access : k => v if v.type == "user" }
|
||||
access_view = { for k, v in var.access : k => v if v.type == "view" }
|
||||
|
||||
identities_view = {
|
||||
for k, v in local.access_view : k => try(
|
||||
zipmap(
|
||||
["project_id", "dataset_id", "table_id"],
|
||||
split("|", var.access_identities[k])
|
||||
),
|
||||
{ project_id = null, dataset_id = null, table_id = null }
|
||||
)
|
||||
}
|
||||
|
||||
authorized_views = merge(
|
||||
{ for access_key, view in local.identities_view : "${view["project_id"]}_${view["dataset_id"]}_${view["table_id"]}" => view },
|
||||
{ for view in var.authorized_views : "${view["project_id"]}_${view["dataset_id"]}_${view["table_id"]}" => view })
|
||||
authorized_datasets = { for dataset in var.authorized_datasets : "${dataset["project_id"]}_${dataset["dataset_id"]}" => dataset }
|
||||
authorized_routines = { for routine in var.authorized_routines : "${routine["project_id"]}_${routine["dataset_id"]}_${routine["routine_id"]}" => routine }
|
||||
|
||||
}
|
||||
|
||||
resource "google_bigquery_dataset" "default" {
|
||||
project = var.project_id
|
||||
dataset_id = var.id
|
||||
friendly_name = var.friendly_name
|
||||
description = var.description
|
||||
labels = var.labels
|
||||
location = var.location
|
||||
|
||||
delete_contents_on_destroy = var.options.delete_contents_on_destroy
|
||||
default_collation = var.options.default_collation
|
||||
default_table_expiration_ms = var.options.default_table_expiration_ms
|
||||
default_partition_expiration_ms = var.options.default_partition_expiration_ms
|
||||
is_case_insensitive = var.options.is_case_insensitive
|
||||
max_time_travel_hours = var.options.max_time_travel_hours
|
||||
dynamic "access" {
|
||||
for_each = var.dataset_access ? local.access_domain : {}
|
||||
content {
|
||||
role = access.value.role
|
||||
domain = try(var.access_identities[access.key])
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "access" {
|
||||
for_each = var.dataset_access ? local.access_group : {}
|
||||
content {
|
||||
role = access.value.role
|
||||
group_by_email = try(var.access_identities[access.key])
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "access" {
|
||||
for_each = var.dataset_access ? local.access_special : {}
|
||||
content {
|
||||
role = access.value.role
|
||||
special_group = try(var.access_identities[access.key])
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "access" {
|
||||
for_each = var.dataset_access ? local.access_user : {}
|
||||
content {
|
||||
role = access.value.role
|
||||
user_by_email = try(var.access_identities[access.key])
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "access" {
|
||||
for_each = var.dataset_access ? local.authorized_views : {}
|
||||
content {
|
||||
view {
|
||||
project_id = each.value.project_id
|
||||
dataset_id = each.value.dataset_id
|
||||
table_id = each.value.table_id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "access" {
|
||||
for_each = var.dataset_access ? local.authorized_datasets : {}
|
||||
content {
|
||||
dataset {
|
||||
dataset {
|
||||
project_id = each.value.project_id
|
||||
dataset_id = each.value.dataset_id
|
||||
}
|
||||
target_types = ["VIEWS"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "access" {
|
||||
for_each = var.dataset_access ? local.authorized_routines : {}
|
||||
content {
|
||||
routine {
|
||||
project_id = each.value.project_id
|
||||
dataset_id = each.value.dataset_id
|
||||
routine_id = each.value.routine_id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "default_encryption_configuration" {
|
||||
for_each = var.encryption_key == null ? [] : [""]
|
||||
content {
|
||||
kms_key_name = var.encryption_key
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_bigquery_dataset_access" "domain" {
|
||||
for_each = var.dataset_access ? {} : local.access_domain
|
||||
provider = google-beta
|
||||
project = var.project_id
|
||||
dataset_id = google_bigquery_dataset.default.dataset_id
|
||||
role = each.value.role
|
||||
domain = try(var.access_identities[each.key])
|
||||
}
|
||||
|
||||
resource "google_bigquery_dataset_access" "group_by_email" {
|
||||
for_each = var.dataset_access ? {} : local.access_group
|
||||
provider = google-beta
|
||||
project = var.project_id
|
||||
dataset_id = google_bigquery_dataset.default.dataset_id
|
||||
role = each.value.role
|
||||
group_by_email = try(var.access_identities[each.key])
|
||||
}
|
||||
|
||||
resource "google_bigquery_dataset_access" "special_group" {
|
||||
for_each = var.dataset_access ? {} : local.access_special
|
||||
provider = google-beta
|
||||
project = var.project_id
|
||||
dataset_id = google_bigquery_dataset.default.dataset_id
|
||||
role = each.value.role
|
||||
special_group = try(var.access_identities[each.key])
|
||||
}
|
||||
|
||||
resource "google_bigquery_dataset_access" "user_by_email" {
|
||||
for_each = var.dataset_access ? {} : local.access_user
|
||||
provider = google-beta
|
||||
project = var.project_id
|
||||
dataset_id = google_bigquery_dataset.default.dataset_id
|
||||
role = each.value.role
|
||||
user_by_email = try(var.access_identities[each.key])
|
||||
}
|
||||
|
||||
resource "google_bigquery_dataset_access" "authorized_views" {
|
||||
for_each = var.dataset_access ? {} : local.authorized_views
|
||||
project = var.project_id
|
||||
dataset_id = google_bigquery_dataset.default.dataset_id
|
||||
view {
|
||||
project_id = each.value.project_id
|
||||
dataset_id = each.value.dataset_id
|
||||
table_id = each.value.table_id
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_bigquery_dataset_access" "authorized_datasets" {
|
||||
for_each = var.dataset_access ? {} : local.authorized_datasets
|
||||
project = var.project_id
|
||||
dataset_id = google_bigquery_dataset.default.dataset_id
|
||||
dataset {
|
||||
dataset {
|
||||
project_id = each.value.project_id
|
||||
dataset_id = each.value.dataset_id
|
||||
}
|
||||
target_types = ["VIEWS"]
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_bigquery_dataset_access" "authorized_routines" {
|
||||
for_each = var.dataset_access ? {} : local.authorized_routines
|
||||
project = var.project_id
|
||||
dataset_id = google_bigquery_dataset.default.dataset_id
|
||||
routine {
|
||||
project_id = each.value.project_id
|
||||
dataset_id = each.value.dataset_id
|
||||
routine_id = each.value.routine_id
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_bigquery_dataset_iam_binding" "bindings" {
|
||||
for_each = var.iam
|
||||
project = var.project_id
|
||||
dataset_id = google_bigquery_dataset.default.dataset_id
|
||||
role = each.key
|
||||
members = each.value
|
||||
}
|
||||
|
||||
resource "google_bigquery_table" "default" {
|
||||
provider = google-beta
|
||||
for_each = var.tables
|
||||
project = var.project_id
|
||||
dataset_id = google_bigquery_dataset.default.dataset_id
|
||||
table_id = each.key
|
||||
friendly_name = each.value.friendly_name
|
||||
description = each.value.description
|
||||
clustering = each.value.options.clustering
|
||||
expiration_time = each.value.options.expiration_time
|
||||
labels = each.value.labels
|
||||
schema = each.value.schema
|
||||
deletion_protection = each.value.deletion_protection
|
||||
|
||||
dynamic "encryption_configuration" {
|
||||
for_each = each.value.options.encryption_key != null ? [""] : []
|
||||
content {
|
||||
kms_key_name = each.value.options.encryption_key
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "range_partitioning" {
|
||||
for_each = try(each.value.partitioning.range, null) != null ? [""] : []
|
||||
content {
|
||||
field = each.value.partitioning.field
|
||||
range {
|
||||
start = each.value.partitioning.range.start
|
||||
end = each.value.partitioning.range.end
|
||||
interval = each.value.partitioning.range.interval
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "time_partitioning" {
|
||||
for_each = try(each.value.partitioning.time, null) != null ? [""] : []
|
||||
content {
|
||||
expiration_ms = each.value.partitioning.time.expiration_ms
|
||||
field = each.value.partitioning.field
|
||||
type = each.value.partitioning.time.type
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_bigquery_table" "views" {
|
||||
depends_on = [google_bigquery_table.default]
|
||||
for_each = var.views
|
||||
project = var.project_id
|
||||
dataset_id = google_bigquery_dataset.default.dataset_id
|
||||
table_id = each.key
|
||||
friendly_name = each.value.friendly_name
|
||||
description = each.value.description
|
||||
labels = each.value.labels
|
||||
deletion_protection = each.value.deletion_protection
|
||||
|
||||
view {
|
||||
query = each.value.query
|
||||
use_legacy_sql = each.value.use_legacy_sql
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
output "dataset" {
|
||||
description = "Dataset resource."
|
||||
value = google_bigquery_dataset.default
|
||||
}
|
||||
|
||||
output "dataset_id" {
|
||||
description = "Dataset id."
|
||||
value = google_bigquery_dataset.default.dataset_id
|
||||
depends_on = [
|
||||
google_bigquery_dataset_access.authorized_datasets,
|
||||
google_bigquery_dataset_access.authorized_routines,
|
||||
google_bigquery_dataset_access.authorized_views,
|
||||
google_bigquery_dataset_access.domain,
|
||||
google_bigquery_dataset_access.group_by_email,
|
||||
google_bigquery_dataset_access.special_group,
|
||||
google_bigquery_dataset_access.user_by_email
|
||||
]
|
||||
}
|
||||
|
||||
output "id" {
|
||||
description = "Fully qualified dataset id."
|
||||
value = google_bigquery_dataset.default.id
|
||||
depends_on = [
|
||||
google_bigquery_dataset_access.authorized_datasets,
|
||||
google_bigquery_dataset_access.authorized_routines,
|
||||
google_bigquery_dataset_access.authorized_views,
|
||||
google_bigquery_dataset_access.domain,
|
||||
google_bigquery_dataset_access.group_by_email,
|
||||
google_bigquery_dataset_access.special_group,
|
||||
google_bigquery_dataset_access.user_by_email
|
||||
]
|
||||
}
|
||||
|
||||
output "self_link" {
|
||||
description = "Dataset self link."
|
||||
value = google_bigquery_dataset.default.self_link
|
||||
depends_on = [
|
||||
google_bigquery_dataset_access.authorized_datasets,
|
||||
google_bigquery_dataset_access.authorized_routines,
|
||||
google_bigquery_dataset_access.authorized_views,
|
||||
google_bigquery_dataset_access.domain,
|
||||
google_bigquery_dataset_access.group_by_email,
|
||||
google_bigquery_dataset_access.special_group,
|
||||
google_bigquery_dataset_access.user_by_email
|
||||
]
|
||||
}
|
||||
|
||||
output "table_ids" {
|
||||
description = "Map of fully qualified table ids keyed by table ids."
|
||||
value = { for k, v in google_bigquery_table.default : v.table_id => v.id }
|
||||
}
|
||||
|
||||
output "tables" {
|
||||
description = "Table resources."
|
||||
value = google_bigquery_table.default
|
||||
}
|
||||
|
||||
output "view_ids" {
|
||||
description = "Map of fully qualified view ids keyed by view ids."
|
||||
value = { for k, v in google_bigquery_table.views : v.table_id => v.id }
|
||||
}
|
||||
|
||||
output "views" {
|
||||
description = "View resources."
|
||||
value = google_bigquery_table.views
|
||||
}
|
|
@ -0,0 +1,173 @@
|
|||
/**
|
||||
* 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 "access" {
|
||||
description = "Map of access rules with role and identity type. Keys are arbitrary and must match those in the `access_identities` variable, types are `domain`, `group`, `special_group`, `user`, `view`."
|
||||
type = map(object({
|
||||
role = string
|
||||
type = string
|
||||
}))
|
||||
default = {}
|
||||
validation {
|
||||
condition = can([
|
||||
for k, v in var.access :
|
||||
index(["domain", "group", "special_group", "user", "view"], v.type)
|
||||
])
|
||||
error_message = "Access type must be one of 'domain', 'group', 'special_group', 'user', 'view'."
|
||||
}
|
||||
}
|
||||
|
||||
variable "access_identities" {
|
||||
description = "Map of access identities used for basic access roles. View identities have the format 'project_id|dataset_id|table_id'."
|
||||
type = map(string)
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "authorized_datasets" {
|
||||
description = "An array of datasets to be authorized on the dataset."
|
||||
type = list(object({
|
||||
dataset_id = string,
|
||||
project_id = string,
|
||||
}))
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "authorized_routines" {
|
||||
description = "An array of authorized routine to be authorized on the dataset."
|
||||
type = list(object({
|
||||
project_id = string,
|
||||
dataset_id = string,
|
||||
routine_id = string
|
||||
}))
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "authorized_views" {
|
||||
description = "An array of views to be authorized on the dataset."
|
||||
type = list(object({
|
||||
dataset_id = string,
|
||||
project_id = string,
|
||||
table_id = string # this is the view id, but we keep table_id to stay consistent as the resource
|
||||
}))
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "dataset_access" {
|
||||
description = "Set access in the dataset resource instead of using separate resources."
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "description" {
|
||||
description = "Optional description."
|
||||
type = string
|
||||
default = "Terraform managed."
|
||||
}
|
||||
|
||||
variable "encryption_key" {
|
||||
description = "Self link of the KMS key that will be used to protect destination table."
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "friendly_name" {
|
||||
description = "Dataset friendly name."
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "iam" {
|
||||
description = "IAM bindings in {ROLE => [MEMBERS]} format. Mutually exclusive with the access_* variables used for basic roles."
|
||||
type = map(list(string))
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "id" {
|
||||
description = "Dataset id."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "labels" {
|
||||
description = "Dataset labels."
|
||||
type = map(string)
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "location" {
|
||||
description = "Dataset location."
|
||||
type = string
|
||||
default = "EU"
|
||||
}
|
||||
|
||||
variable "options" {
|
||||
description = "Dataset options."
|
||||
type = object({
|
||||
default_collation = optional(string)
|
||||
default_table_expiration_ms = optional(number)
|
||||
default_partition_expiration_ms = optional(number)
|
||||
delete_contents_on_destroy = optional(bool, false)
|
||||
is_case_insensitive = optional(bool)
|
||||
max_time_travel_hours = optional(number, 168)
|
||||
})
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "project_id" {
|
||||
description = "Id of the project where datasets will be created."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "tables" {
|
||||
description = "Table definitions. Options and partitioning default to null. Partitioning can only use `range` or `time`, set the unused one to null."
|
||||
type = map(object({
|
||||
deletion_protection = optional(bool)
|
||||
description = optional(string, "Terraform managed.")
|
||||
friendly_name = optional(string)
|
||||
labels = optional(map(string), {})
|
||||
schema = optional(string)
|
||||
options = optional(object({
|
||||
clustering = optional(list(string))
|
||||
encryption_key = optional(string)
|
||||
expiration_time = optional(number)
|
||||
}), {})
|
||||
partitioning = optional(object({
|
||||
field = optional(string)
|
||||
range = optional(object({
|
||||
end = number
|
||||
interval = number
|
||||
start = number
|
||||
}))
|
||||
time = optional(object({
|
||||
expiration_ms = number
|
||||
type = string
|
||||
}))
|
||||
}))
|
||||
}))
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "views" {
|
||||
description = "View definitions."
|
||||
type = map(object({
|
||||
query = string
|
||||
deletion_protection = optional(bool)
|
||||
description = optional(string, "Terraform managed.")
|
||||
friendly_name = optional(string)
|
||||
labels = optional(map(string), {})
|
||||
use_legacy_sql = optional(bool)
|
||||
}))
|
||||
default = {}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
# 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
|
||||
#
|
||||
# https://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.
|
||||
|
||||
terraform {
|
||||
required_version = ">= 1.4.4"
|
||||
required_providers {
|
||||
google = {
|
||||
source = "hashicorp/google"
|
||||
version = ">= 4.82.0" # tftest
|
||||
}
|
||||
google-beta = {
|
||||
source = "hashicorp/google-beta"
|
||||
version = ">= 4.82.0" # tftest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue