Merge branch 'master' of github.com:GoogleCloudPlatform/cloud-foundation-fabric
This commit is contained in:
commit
c494715e9a
|
@ -4,7 +4,7 @@ This section provides **[networking blueprints](./networking/)** that implement
|
||||||
|
|
||||||
Currently available blueprints:
|
Currently available blueprints:
|
||||||
|
|
||||||
- **apigee** - [Apigee Hybrid on GKE](./apigee/hybrid-gke/), [Apigee X analytics in BigQuery](./apigee/bigquery-analytics), [Apigee network patterns](./apigee/network-patterns/)
|
- **apigee** - [Apigee X foundations](./apigee/apigee-x-foundations/). [Apigee Hybrid on GKE](./apigee/hybrid-gke/), [Apigee X analytics in BigQuery](./apigee/bigquery-analytics), [Apigee network patterns](./apigee/network-patterns/)
|
||||||
- **cloud operations** - [Active Directory Federation Services](./cloud-operations/adfs), [Cloud Asset Inventory feeds for resource change tracking and remediation](./cloud-operations/asset-inventory-feed-remediation), [Fine-grained Cloud DNS IAM via Service Directory](./cloud-operations/dns-fine-grained-iam), [Cloud DNS & Shared VPC design](./cloud-operations/dns-shared-vpc), [Delegated Role Grants](./cloud-operations/iam-delegated-role-grants), [Network Quota Monitoring](./cloud-operations/network-quota-monitoring), [Managing on-prem service account keys by uploading public keys](./cloud-operations/onprem-sa-key-management), [Compute Image builder with Hashicorp Packer](./cloud-operations/packer-image-builder), [Packer example](./cloud-operations/packer-image-builder/packer), [Compute Engine quota monitoring](./cloud-operations/compute-quota-monitoring), [Scheduled Cloud Asset Inventory Export to Bigquery](./cloud-operations/scheduled-asset-inventory-export-bq), [Configuring workload identity federation with Terraform Cloud/Enterprise workflows](./cloud-operations/terraform-cloud-dynamic-credentials), [TCP healthcheck and restart for unmanaged GCE instances](./cloud-operations/unmanaged-instances-healthcheck), [Migrate for Compute Engine (v5) blueprints](./cloud-operations/vm-migration), [Configuring workload identity federation to access Google Cloud resources from apps running on Azure](./cloud-operations/workload-identity-federation)
|
- **cloud operations** - [Active Directory Federation Services](./cloud-operations/adfs), [Cloud Asset Inventory feeds for resource change tracking and remediation](./cloud-operations/asset-inventory-feed-remediation), [Fine-grained Cloud DNS IAM via Service Directory](./cloud-operations/dns-fine-grained-iam), [Cloud DNS & Shared VPC design](./cloud-operations/dns-shared-vpc), [Delegated Role Grants](./cloud-operations/iam-delegated-role-grants), [Network Quota Monitoring](./cloud-operations/network-quota-monitoring), [Managing on-prem service account keys by uploading public keys](./cloud-operations/onprem-sa-key-management), [Compute Image builder with Hashicorp Packer](./cloud-operations/packer-image-builder), [Packer example](./cloud-operations/packer-image-builder/packer), [Compute Engine quota monitoring](./cloud-operations/compute-quota-monitoring), [Scheduled Cloud Asset Inventory Export to Bigquery](./cloud-operations/scheduled-asset-inventory-export-bq), [Configuring workload identity federation with Terraform Cloud/Enterprise workflows](./cloud-operations/terraform-cloud-dynamic-credentials), [TCP healthcheck and restart for unmanaged GCE instances](./cloud-operations/unmanaged-instances-healthcheck), [Migrate for Compute Engine (v5) blueprints](./cloud-operations/vm-migration), [Configuring workload identity federation to access Google Cloud resources from apps running on Azure](./cloud-operations/workload-identity-federation)
|
||||||
- **data solutions** - [GCE and GCS CMEK via centralized Cloud KMS](./data-solutions/cmek-via-centralized-kms), [Cloud Composer version 2 private instance, supporting Shared VPC and external CMEK key](./data-solutions/composer-2), [Cloud SQL instance with multi-region read replicas](./data-solutions/cloudsql-multiregion), [Data Platform](./data-solutions/data-platform-foundations), [Minimal Data Platform](./data-solutions/data-platform-minimal), [Spinning up a foundation data pipeline on Google Cloud using Cloud Storage, Dataflow and BigQuery](./data-solutions/gcs-to-bq-with-least-privileges), [#SQL Server Always On Groups blueprint](./data-solutions/sqlserver-alwayson), [Data Playground](./data-solutions/data-playground), [MLOps with Vertex AI](./data-solutions/vertex-mlops), [Shielded Folder](./data-solutions/shielded-folder), [BigQuery ML and Vertex AI Pipeline](./data-solutions/bq-ml)
|
- **data solutions** - [GCE and GCS CMEK via centralized Cloud KMS](./data-solutions/cmek-via-centralized-kms), [Cloud Composer version 2 private instance, supporting Shared VPC and external CMEK key](./data-solutions/composer-2), [Cloud SQL instance with multi-region read replicas](./data-solutions/cloudsql-multiregion), [Data Platform](./data-solutions/data-platform-foundations), [Minimal Data Platform](./data-solutions/data-platform-minimal), [Spinning up a foundation data pipeline on Google Cloud using Cloud Storage, Dataflow and BigQuery](./data-solutions/gcs-to-bq-with-least-privileges), [#SQL Server Always On Groups blueprint](./data-solutions/sqlserver-alwayson), [Data Playground](./data-solutions/data-playground), [MLOps with Vertex AI](./data-solutions/vertex-mlops), [Shielded Folder](./data-solutions/shielded-folder), [BigQuery ML and Vertex AI Pipeline](./data-solutions/bq-ml)
|
||||||
- **factories** - [Fabric resource factories](./factories)
|
- **factories** - [Fabric resource factories](./factories)
|
||||||
|
|
|
@ -4,9 +4,15 @@ The blueprints in this folder contain a variety of deployment scenarios for Apig
|
||||||
|
|
||||||
## Blueprints
|
## Blueprints
|
||||||
|
|
||||||
|
### Apigee X foundations
|
||||||
|
|
||||||
|
<a href="./apigee-x-foundations/" title="Apigee X foundations"><img src="./apigee-x-foundations/diagram1.png" align="left" width="280px"></a> This [blueprint](./apigee-x-foundations/) creates all the resources necessary to set up Apigee X on Google Cloud.
|
||||||
|
|
||||||
|
<br clear="left">
|
||||||
|
|
||||||
### Apigee Hybrid on GKE
|
### Apigee Hybrid on GKE
|
||||||
|
|
||||||
<a href="./hybrid-gke/" title="Apigee Hybrid on GKE"><img src="./hybrid-gke/diagram.png" align="left" width="280px"></a> This [blueprint](./hybrid-gke/) shows how to do a non-prod deployment of Apigee Hybrid on GKE(../factories/net-vpc-firewall-yaml/).
|
<a href="./hybrid-gke/" title="Apigee Hybrid on GKE"><img src="./hybrid-gke/diagram.png" align="left" width="280px"></a> This [blueprint](./hybrid-gke/) shows how to do a non-prod deployment of Apigee Hybrid on GKE.
|
||||||
|
|
||||||
<br clear="left">
|
<br clear="left">
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,481 @@
|
||||||
|
# Apigee X Foundations
|
||||||
|
|
||||||
|
This blueprint creates all the resources necessary to set up Apigee X on Google Cloud.
|
||||||
|
|
||||||
|
Apigee can be exposed to clients using Regional Internal Application Load Balancer, Global External Application Load Balancer or both. When using the Regional Internal Application Load Balancer, used self-managed certificates (incuding self-signed certificates generated in this same module). When using the Global External Application Load Balancer Google-managed certificates or self-managed certificates (including self-signed certificates generated in this same module). When using Cross-region Internal Application Load Balancer a certificate manager needs to be used and it needs to be created in the same project as Apigee.
|
||||||
|
|
||||||
|
Find below a few examples of different Apigee architectures that can be created using this module.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
* [Examples](#examples)
|
||||||
|
* [Apigee X in service project with shared VPC peered and exposed with Global External Application LB and Regional Internal Application LB](#apigee-x-in-service-project-with-shared-vpc-peered-and-exposed-with-global-external-application-lb-and-regional-internal-application-lb)
|
||||||
|
* [Apigee X in service project with local VPC peered and exposed using Global LB and Internal Cross-region Application LB](#apigee-x-in-service-project-with-local-vpc-peered-and-exposed-using-global-lb-and-internal-cross-region-application-lb)
|
||||||
|
* [Apigee X in service project with peering disabled and exposed using Global LB](#apigee-x-in-service-project-with-peering-disabled-and-exposed-using-global-lb)
|
||||||
|
* [Apigee X in standalone project with peering enabled and exposed with Regional Internal LB](#apigee-x-in-standalone-project-with-peering-enabled-and-exposed-with-regional-internal-lb)
|
||||||
|
* [Apigee X in standalone project with peering disabled and exposed using Global External Application LB](#apigee-x-in-standalone-project-with-peering-disabled-and-exposed-using-global-external-application-lb)
|
||||||
|
* [Variables](#files)
|
||||||
|
* [Variables](#variables)
|
||||||
|
* [Outputs](#outputs)
|
||||||
|
|
||||||
|
### Apigee X in service project with shared VPC peered and exposed with Global External Application LB and Regional Internal Application LB
|
||||||
|
|
||||||
|
![Diagram](./diagram1.png)
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
module "apigee-x-foundations" {
|
||||||
|
source = "./fabric/blueprints/apigee/apigee-x-foundations"
|
||||||
|
project_config = {
|
||||||
|
billing_account_id = var.billing_account_id
|
||||||
|
parent = var.folder_id
|
||||||
|
name = var.project_id
|
||||||
|
iam = {
|
||||||
|
"roles/apigee.admin" = ["group:apigee-admins@myorg.com"]
|
||||||
|
}
|
||||||
|
shared_vpc_service_config = {
|
||||||
|
host_project = "my-host-project"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
apigee_config = {
|
||||||
|
addons_config = {
|
||||||
|
api_security = true
|
||||||
|
}
|
||||||
|
organization = {
|
||||||
|
analytics_region = "europe-west1"
|
||||||
|
}
|
||||||
|
envgroups = {
|
||||||
|
apis = [
|
||||||
|
"apis.external.myorg.com",
|
||||||
|
"apis.internal.myorg.com"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
environments = {
|
||||||
|
apis = {
|
||||||
|
envgroups = ["apis"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
instances = {
|
||||||
|
europe-west1 = {
|
||||||
|
external = true
|
||||||
|
runtime_ip_cidr_range = "10.0.0.0/22"
|
||||||
|
troubleshooting_ip_cidr_range = "192.168.0.0/18"
|
||||||
|
environments = ["apis"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
endpoint_attachments = {
|
||||||
|
endpoint-backend-ew1 = {
|
||||||
|
region = "europe-west1"
|
||||||
|
service_attachment = "projects/a58971796302e0142p-tp/regions/europe-west4/serviceAttachments/my-service-attachment-ew1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
network_config = {
|
||||||
|
shared_vpc = {
|
||||||
|
name = "my-shared-vpc"
|
||||||
|
subnets = {
|
||||||
|
europe-west1 = "projects/my-host-project/regions/europe-west4/subnetworks/my-subnet-ew1"
|
||||||
|
}
|
||||||
|
subnets_psc = {
|
||||||
|
europe-west1 = "projects/my-host-project/regions/europe-west4/subnetworks/my-subnet-psc-ew1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ext_lb_config = {
|
||||||
|
ssl_certificates = {
|
||||||
|
create_configs = {
|
||||||
|
default = {
|
||||||
|
certificate = "PEM-Encoded certificate string"
|
||||||
|
private_key = "PEM-Encoded private key string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int_lb_config = {
|
||||||
|
ssl_certificates = {
|
||||||
|
create_configs = {
|
||||||
|
default = {
|
||||||
|
certificate = "PEM-Encoded certificate string"
|
||||||
|
private_key = "PEM-Encoded private key string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# tftest modules=7 resources=42
|
||||||
|
```
|
||||||
|
|
||||||
|
### Apigee X in service project with local VPC peered and exposed using Global LB and Internal Cross-region Application LB
|
||||||
|
|
||||||
|
![Diagram](./diagram2.png)
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
module "apigee-x-foundations" {
|
||||||
|
source = "./fabric/blueprints/apigee/apigee-x-foundations"
|
||||||
|
project_config = {
|
||||||
|
billing_account_id = "1234-5678-0000"
|
||||||
|
parent = "folders/123456789"
|
||||||
|
name = "my-project"
|
||||||
|
iam = {
|
||||||
|
"roles/apigee.admin" = ["group:apigee-admins@myorg.com"]
|
||||||
|
}
|
||||||
|
shared_vpc_service_config = {
|
||||||
|
host_project = "my-host-project"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
apigee_config = {
|
||||||
|
addons_config = {
|
||||||
|
api_security = true
|
||||||
|
}
|
||||||
|
organization = {
|
||||||
|
analytics_region = "europe-west1"
|
||||||
|
billing_type = "PAYG"
|
||||||
|
}
|
||||||
|
envgroups = {
|
||||||
|
apis = [
|
||||||
|
"apis.external.myorg.com",
|
||||||
|
"apis.internal.myorg.com"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
environments = {
|
||||||
|
apis = {
|
||||||
|
envgroups = ["apis"]
|
||||||
|
type = "COMPREHENSIVE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
instances = {
|
||||||
|
europe-west1 = {
|
||||||
|
runtime_ip_cidr_range = "10.0.0.0/22"
|
||||||
|
troubleshooting_ip_cidr_range = "192.168.0.0/28"
|
||||||
|
environments = ["apis"]
|
||||||
|
}
|
||||||
|
europe-west4 = {
|
||||||
|
runtime_ip_cidr_range = "10.0.4.0/22"
|
||||||
|
troubleshooting_ip_cidr_range = "192.168.0.16/28"
|
||||||
|
environments = ["apis"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
endpoint_attachments = {
|
||||||
|
endpoint-backend-ew1 = {
|
||||||
|
region = "europe-west1"
|
||||||
|
service_attachment = "projects/a58971796302e0142p-tp/regions/europe-west1/serviceAttachments/my-service-attachment-ew1"
|
||||||
|
dns_names = [
|
||||||
|
"backend.myorg.com"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
endpoint-backend-ew4 = {
|
||||||
|
region = "europe-west1"
|
||||||
|
service_attachment = "projects/a58971796302e0142p-tp/regions/europe-west4/serviceAttachments/my-service-attachment-ew4"
|
||||||
|
dns_names = [
|
||||||
|
"backend.myorg.com"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
network_config = {
|
||||||
|
shared_vpc = {
|
||||||
|
name = "my-shared-vpc"
|
||||||
|
subnets = {
|
||||||
|
europe-west1 = "projects/my-host-project/regions/europe-west4/subnetworks/my-subnet-eu1"
|
||||||
|
europe-west4 = "projects/my-host-project/regions/europe-west4/subnetworks/my-subnet-eu4"
|
||||||
|
}
|
||||||
|
subnets_psc = {
|
||||||
|
europe-west1 = "projects/my-host-project/regions/europe-west4/subnetworks/my-subnet-psc-eu1"
|
||||||
|
europe-west4 = "projects/my-host-project/regions/europe-west4/subnetworks/my-subnet-psc-eu4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
apigee_vpc = {
|
||||||
|
auto_create = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ext_lb_config = {
|
||||||
|
ssl_certificates = {
|
||||||
|
create_configs = {
|
||||||
|
default = {
|
||||||
|
certificate = "PEM-Encoded certificate string"
|
||||||
|
private_key = "PEM-Encoded private key string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int_cross_region_lb_config = {
|
||||||
|
certificate_manager_certificates = [
|
||||||
|
"projects/myprj/locations/global/certificates/certificate"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# tftest modules=10 resources=62
|
||||||
|
```
|
||||||
|
|
||||||
|
### Apigee X in service project with peering disabled and exposed using Global LB
|
||||||
|
|
||||||
|
![Diagram](./diagram3.png)
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
module "apigee-x-foundations" {
|
||||||
|
source = "./fabric/blueprints/apigee/apigee-x-foundations"
|
||||||
|
project_config = {
|
||||||
|
billing_account_id = "1234-5678-0000"
|
||||||
|
parent = "folders/123456789"
|
||||||
|
name = "my-project"
|
||||||
|
iam = {
|
||||||
|
"roles/apigee.admin" = ["group:apigee-admins@myorg.com"]
|
||||||
|
}
|
||||||
|
shared_vpc_service_config = {
|
||||||
|
host_project = "my-host-project"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
apigee_config = {
|
||||||
|
addons_config = {
|
||||||
|
api_security = true
|
||||||
|
}
|
||||||
|
organization = {
|
||||||
|
analytics_region = "europe-west1"
|
||||||
|
disable_vpc_peering = true
|
||||||
|
}
|
||||||
|
envgroups = {
|
||||||
|
apis = [
|
||||||
|
"apis.external.myorg.com"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
environments = {
|
||||||
|
apis = {
|
||||||
|
envgroups = ["apis"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
instances = {
|
||||||
|
europe-west1 = {
|
||||||
|
runtime_ip_cidr_range = "10.0.0.0/22"
|
||||||
|
troubleshooting_ip_cidr_range = "192.168.0.0/18"
|
||||||
|
environments = ["apis"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
endpoint_attachments = {
|
||||||
|
endpoint-backend-ew1 = {
|
||||||
|
region = "europe-west1"
|
||||||
|
service_attachment = "projects/a58971796302e0142p-tp/regions/europe-west4/serviceAttachments/my-service-attachment-ew1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
disable_vpc_peering = true
|
||||||
|
}
|
||||||
|
network_config = {
|
||||||
|
shared_vpc = {
|
||||||
|
name = "my-shared-vpc"
|
||||||
|
subnets = {
|
||||||
|
europe-west1 = "projects/my-host-project/regions/europe-west4/subnetworks/my-subnet-ew1"
|
||||||
|
}
|
||||||
|
subnets_psc = {
|
||||||
|
europe-west1 = "projects/my-host-project/regions/europe-west4/subnetworks/my-subnet-psc-ew1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ext_lb_config = {
|
||||||
|
ssl_certificates = {
|
||||||
|
create_configs = {
|
||||||
|
default = {
|
||||||
|
certificate = "PEM-Encoded certificate string"
|
||||||
|
private_key = "PEM-Encoded private key string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# tftest modules=6 resources=36
|
||||||
|
```
|
||||||
|
|
||||||
|
### Apigee X in standalone project with peering enabled and exposed with Regional Internal LB
|
||||||
|
|
||||||
|
![Diagram](./diagram4.png)
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
module "apigee-x-foundations" {
|
||||||
|
source = "./fabric/blueprints/apigee/apigee-x-foundations"
|
||||||
|
project_config = {
|
||||||
|
billing_account_id = "1234-5678-0000"
|
||||||
|
parent = "folders/123456789"
|
||||||
|
name = "my-project"
|
||||||
|
iam = {
|
||||||
|
"roles/apigee.admin" = ["group:apigee-admins@myorg.com"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
apigee_config = {
|
||||||
|
addons_config = {
|
||||||
|
api_security = true
|
||||||
|
}
|
||||||
|
organization = {
|
||||||
|
analytics_region = "europe-west1"
|
||||||
|
}
|
||||||
|
envgroups = {
|
||||||
|
apis = [
|
||||||
|
"apis.internal.myorg.com"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
environments = {
|
||||||
|
apis = {
|
||||||
|
envgroups = ["apis"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
instances = {
|
||||||
|
europe-west1 = {
|
||||||
|
runtime_ip_cidr_range = "172.16.0.0/22"
|
||||||
|
troubleshooting_ip_cidr_range = "192.168.0.0/18"
|
||||||
|
environments = ["apis"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
endpoint_attachments = {
|
||||||
|
endpoint-backend-ew1 = {
|
||||||
|
region = "europe-west1"
|
||||||
|
service_attachment = "projects/a58971796302e0142p-tp/regions/europe-west4/serviceAttachments/my-service-attachment-ew1"
|
||||||
|
dns_names = [
|
||||||
|
"backend.myorg.com"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
network_config = {
|
||||||
|
apigee_vpc = {
|
||||||
|
subnets = {
|
||||||
|
europe-west1 = {
|
||||||
|
ip_cidr_range = "10.0.0.0/29"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subnets_proxy_only = {
|
||||||
|
europe-west1 = {
|
||||||
|
ip_cidr_range = "10.1.0.0/26"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subnets_psc = {
|
||||||
|
europe-west1 = {
|
||||||
|
ip_cidr_range = "10.0.1.0/29"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int_lb_config = {
|
||||||
|
ssl_certificates = {
|
||||||
|
create_configs = {
|
||||||
|
default = {
|
||||||
|
certificate = "PEM-Encoded certificate string"
|
||||||
|
private_key = "PEM-Encoded private key string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# tftest modules=8 resources=48
|
||||||
|
```
|
||||||
|
|
||||||
|
### Apigee X in standalone project with peering disabled and exposed using Global External Application LB
|
||||||
|
|
||||||
|
![Diagram](./diagram5.png)
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
module "apigee-x-foundations" {
|
||||||
|
source = "./fabric/blueprints/apigee/apigee-x-foundations"
|
||||||
|
project_config = {
|
||||||
|
billing_account_id = "1234-5678-0000"
|
||||||
|
parent = "folders/123456789"
|
||||||
|
name = "my-project"
|
||||||
|
iam = {
|
||||||
|
"roles/apigee.admin" = ["group:apigee-admins@myorg.com"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
apigee_config = {
|
||||||
|
addons_config = {
|
||||||
|
api_security = true
|
||||||
|
}
|
||||||
|
organization = {
|
||||||
|
analytics_region = "europe-west1"
|
||||||
|
disable_vpc_peering = true
|
||||||
|
}
|
||||||
|
envgroups = {
|
||||||
|
apis = [
|
||||||
|
"apis.external.myorg.com",
|
||||||
|
"apis.internal.myorg.com"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
environments = {
|
||||||
|
apis = {
|
||||||
|
envgroups = ["apis"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
instances = {
|
||||||
|
europe-west1 = {
|
||||||
|
environments = ["apis"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
endpoint_attachments = {
|
||||||
|
endpoint-backend-ew1 = {
|
||||||
|
region = "europe-west1"
|
||||||
|
service_attachment = "projects/a58971796302e0142p-tp/regions/europe-west4/serviceAttachments/my-service-attachment-ew1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
disable_vpc_peering = true
|
||||||
|
}
|
||||||
|
network_config = {
|
||||||
|
apigee_vpc = {
|
||||||
|
auto_create = true
|
||||||
|
subnets = {
|
||||||
|
europe-west1 = {
|
||||||
|
ip_cidr_range = "10.0.0.0/29"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subnets_psc = {
|
||||||
|
europe-west1 = {
|
||||||
|
ip_cidr_range = "10.0.1.0/29"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ext_lb_config = {
|
||||||
|
ssl_certificates = {
|
||||||
|
create_configs = {
|
||||||
|
default = {
|
||||||
|
certificate = "PEM-Encoded certificate string"
|
||||||
|
private_key = "PEM-Encoded private key string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enable_monitoring = true
|
||||||
|
}
|
||||||
|
# tftest modules=8 resources=55
|
||||||
|
```
|
||||||
|
|
||||||
|
<!-- TFDOC OPTS files:1 show_extra:1 -->
|
||||||
|
<!-- BEGIN TFDOC -->
|
||||||
|
## Files
|
||||||
|
|
||||||
|
| name | description | modules | resources |
|
||||||
|
|---|---|---|---|
|
||||||
|
| [apigee.tf](./apigee.tf) | None | <code>apigee</code> | |
|
||||||
|
| [dns.tf](./dns.tf) | None | | |
|
||||||
|
| [kms.tf](./kms.tf) | None | <code>kms</code> | <code>random_id</code> |
|
||||||
|
| [main.tf](./main.tf) | Module-level locals and resources. | <code>net-vpc</code> · <code>project</code> | |
|
||||||
|
| [monitoring.tf](./monitoring.tf) | None | <code>cloud-function-v2</code> | |
|
||||||
|
| [northbound.tf](./northbound.tf) | None | <code>net-lb-app-ext</code> · <code>net-lb-app-int</code> · <code>net-lb-app-int-cross-region</code> | <code>google_compute_region_network_endpoint_group</code> · <code>google_compute_security_policy</code> |
|
||||||
|
| [outputs.tf](./outputs.tf) | Module outputs. | | |
|
||||||
|
| [variables.tf](./variables.tf) | Module variables. | | |
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
| name | description | type | required | default | producer |
|
||||||
|
|---|---|:---:|:---:|:---:|:---:|
|
||||||
|
| [apigee_config](variables.tf#L17) | Apigee configuration. | <code title="object({ addons_config = optional(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) })) organization = object({ display_name = optional(string) description = optional(string, "Terraform-managed") billing_type = optional(string) database_encryption_key = optional(string) analytics_region = optional(string, "europe-west1") retention = optional(string) disable_vpc_peering = optional(bool, false) }) envgroups = optional(map(list(string)), {}) environments = optional(map(object({ description = optional(string) display_name = optional(string) envgroups = optional(list(string), []) iam = optional(map(list(string)), {}) iam_bindings = optional(map(object({ role = string members = list(string) condition = optional(object({ expression = string title = string description = optional(string) })) })), {}) iam_bindings_additive = optional(map(object({ role = string member = string condition = optional(object({ expression = string title = string description = optional(string) })) })), {}) node_config = optional(object({ min_node_count = optional(number) max_node_count = optional(number) }), {}) type = optional(string) })), {}) instances = optional(map(object({ disk_encryption_key = optional(string) environments = optional(list(string), []) external = optional(bool, true) runtime_ip_cidr_range = optional(string) troubleshooting_ip_cidr_range = optional(string) })), {}) endpoint_attachments = optional(map(object({ region = string service_attachment = string dns_names = optional(list(string), []) })), {}) })">object({…})</code> | ✓ | | |
|
||||||
|
| [project_config](variables.tf#L271) | Project configuration. | <code title="object({ billing_account_id = optional(string) compute_metadata = optional(map(string), {}) contacts = optional(map(list(string)), {}) custom_roles = optional(map(list(string)), {}) default_service_account = optional(string, "keep") descriptive_name = optional(string) iam = optional(map(list(string)), {}) group_iam = optional(map(list(string)), {}) iam_bindings = optional(map(object({ role = string members = list(string) condition = optional(object({ expression = string title = string description = optional(string) })) })), {}) iam_bindings_additive = optional(map(object({ role = string member = string condition = optional(object({ expression = string title = string description = optional(string) })) })), {}) labels = optional(map(string), {}) lien_reason = optional(string) logging_data_access = optional(map(map(list(string))), {}) log_exclusions = optional(map(string), {}) logging_sinks = optional(map(object({ bq_partitioned_table = optional(bool) description = optional(string) destination = string disabled = optional(bool, false) exclusions = optional(map(string), {}) filter = string iam = optional(bool, true) type = string unique_writer = optional(bool, true) })), {}) metric_scopes = optional(list(string), []) name = string org_policies = optional(map(object({ inherit_from_parent = optional(bool) # for list policies only. reset = optional(bool) rules = optional(list(object({ allow = optional(object({ all = optional(bool) values = optional(list(string)) })) deny = optional(object({ all = optional(bool) values = optional(list(string)) })) enforce = optional(bool) # for boolean policies only. condition = optional(object({ description = optional(string) expression = optional(string) location = optional(string) title = optional(string) }), {}) })), []) })), {}) parent = optional(string) prefix = optional(string) project_create = optional(bool, true) vpc_sc = optional(object({ perimeter_name = string perimeter_bridges = optional(list(string), []) is_dry_run = optional(bool, false) })) services = optional(list(string), []) shared_vpc_host_config = optional(object({ enabled = bool service_projects = optional(list(string), []) })) shared_vpc_service_config = optional(object({ host_project = string service_identity_iam = optional(map(list(string)), {}) service_iam_grants = optional(list(string), []) })) skip_delete = optional(bool, false) tag_bindings = optional(map(string)) })">object({…})</code> | ✓ | | |
|
||||||
|
| [enable_monitoring](variables.tf#L87) | Boolean flag indicating whether an custom metric to monitor instances should be created in Cloud monitoring. | <code>bool</code> | | <code>false</code> | |
|
||||||
|
| [ext_lb_config](variables.tf#L93) | External application load balancer configuration. | <code title="object({ log_sample_rate = optional(number) outlier_detection = optional(object({ consecutive_errors = optional(number) consecutive_gateway_failure = optional(number) enforcing_consecutive_errors = optional(number) enforcing_consecutive_gateway_failure = optional(number) enforcing_success_rate = optional(number) max_ejection_percent = optional(number) success_rate_minimum_hosts = optional(number) success_rate_request_volume = optional(number) success_rate_stdev_factor = optional(number) base_ejection_time = optional(object({ seconds = number nanos = optional(number) })) interval = optional(object({ seconds = number nanos = optional(number) })) })) security_policy = optional(object({ advanced_options_config = optional(object({ json_parsing = optional(object({ enable = optional(bool, false) content_types = optional(list(string)) })) log_level = optional(string) })) adaptive_protection_config = optional(object({ layer_7_ddos_defense_config = optional(object({ enable = optional(bool, false) rule_visibility = optional(string) })) auto_deploy_config = optional(object({ load_threshold = optional(number) confidence_threshold = optional(number) impacted_baseline_threshold = optional(number) expiration_sec = optional(number) })) })) rate_limit_threshold = optional(object({ count = number interval_sec = number })) forbidden_src_ip_ranges = optional(list(string), []) forbidden_regions = optional(list(string), []) preconfigured_waf_rules = optional(map(object({ sensitivity = optional(number) opt_in_rule_ids = optional(list(string), []) opt_out_rule_ids = optional(list(string), []) }))) })) ssl_certificates = object({ certificate_ids = optional(list(string), []) create_configs = optional(map(object({ certificate = string private_key = string })), {}) managed_configs = optional(map(object({ domains = list(string) description = optional(string) })), {}) self_signed_configs = optional(list(string), null) }) })">object({…})</code> | | <code>null</code> | |
|
||||||
|
| [int_cross_region_lb_config](variables.tf#L164) | Internal application load balancer configuration. | <code title="object({ log_sample_rate = optional(number) outlier_detection = optional(object({ consecutive_errors = optional(number) consecutive_gateway_failure = optional(number) enforcing_consecutive_errors = optional(number) enforcing_consecutive_gateway_failure = optional(number) enforcing_success_rate = optional(number) max_ejection_percent = optional(number) success_rate_minimum_hosts = optional(number) success_rate_request_volume = optional(number) success_rate_stdev_factor = optional(number) base_ejection_time = optional(object({ seconds = number nanos = optional(number) })) interval = optional(object({ seconds = number nanos = optional(number) })) })) certificate_manager_certificates = optional(list(string)) })">object({…})</code> | | <code>null</code> | |
|
||||||
|
| [int_lb_config](variables.tf#L192) | Internal application load balancer configuration. | <code title="object({ log_sample_rate = optional(number) outlier_detection = optional(object({ consecutive_errors = optional(number) consecutive_gateway_failure = optional(number) enforcing_consecutive_errors = optional(number) enforcing_consecutive_gateway_failure = optional(number) enforcing_success_rate = optional(number) max_ejection_percent = optional(number) success_rate_minimum_hosts = optional(number) success_rate_request_volume = optional(number) success_rate_stdev_factor = optional(number) base_ejection_time = optional(object({ seconds = number nanos = optional(number) })) interval = optional(object({ seconds = number nanos = optional(number) })) })) ssl_certificates = object({ certificate_ids = optional(list(string), []) create_configs = optional(map(object({ certificate = string private_key = string })), {}) self_signed_configs = optional(list(string), []) }) })">object({…})</code> | | <code>null</code> | |
|
||||||
|
| [network_config](variables.tf#L228) | Network configuration. | <code title="object({ shared_vpc = optional(object({ name = string subnets = map(string) subnets_psc = map(string) })) apigee_vpc = optional(object({ name = optional(string) auto_create = optional(bool, true) subnets = optional(map(object({ id = optional(string) name = optional(string) ip_cidr_range = optional(string) })), {}) subnets_proxy_only = optional(map(object({ name = optional(string) ip_cidr_range = string })), {}) subnets_psc = optional(map(object({ id = optional(string) name = optional(string) ip_cidr_range = optional(string) })), {}) })) })">object({…})</code> | | <code>{}</code> | |
|
||||||
|
|
||||||
|
## Outputs
|
||||||
|
|
||||||
|
| name | description | sensitive | consumers |
|
||||||
|
|---|---|:---:|---|
|
||||||
|
| [endpoint_attachment_hosts](outputs.tf#L17) | Endpoint attachment hosts. | | |
|
||||||
|
| [ext_lb_ip_address](outputs.tf#L22) | External IP address. | | |
|
||||||
|
| [instance_service_attachments](outputs.tf#L27) | Instance service attachments. | | |
|
||||||
|
| [int_cross_region_lb_ip_addresses](outputs.tf#L32) | Internal IP addresses. | | |
|
||||||
|
| [int_lb_ip_addresses](outputs.tf#L37) | Internal IP addresses. | | |
|
||||||
|
| [project_id](outputs.tf#L42) | Project. | | |
|
||||||
|
<!-- END TFDOC -->
|
|
@ -0,0 +1,37 @@
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module "apigee" {
|
||||||
|
source = "../../../modules/apigee"
|
||||||
|
project_id = module.project.project_id
|
||||||
|
organization = merge(var.apigee_config.organization, var.network_config.apigee_vpc != null && !var.apigee_config.organization.disable_vpc_peering ? {
|
||||||
|
authorized_network = module.apigee_vpc[0].id
|
||||||
|
} : var.network_config.shared_vpc != null && !var.apigee_config.organization.disable_vpc_peering ? {
|
||||||
|
authorized_network = module.shared_vpc[0].id
|
||||||
|
} : {},
|
||||||
|
var.apigee_config.organization.database_encryption_key == null ? {} : {
|
||||||
|
database_encryption_key = module.database_kms[0].keys["database-key"].id
|
||||||
|
}, {
|
||||||
|
runtime_type = "CLOUD"
|
||||||
|
})
|
||||||
|
envgroups = var.apigee_config.envgroups
|
||||||
|
environments = var.apigee_config.environments
|
||||||
|
instances = { for k, v in var.apigee_config.instances : k => merge(v, v.disk_encryption_key == null ? {
|
||||||
|
disk_encryption_key = module.disks_kms[k].key_ids["disk-key"]
|
||||||
|
} : {}) }
|
||||||
|
endpoint_attachments = var.apigee_config.endpoint_attachments
|
||||||
|
addons_config = var.apigee_config.addons_config
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
Binary file not shown.
After Width: | Height: | Size: 51 KiB |
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
|
@ -0,0 +1,56 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2024 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 {
|
||||||
|
dns_names = [for v1 in flatten([for k2, v2 in var.apigee_config.endpoint_attachments :
|
||||||
|
[for v3 in coalesce(v2.dns_names, []) : {
|
||||||
|
endpoint_attachment = k2
|
||||||
|
dns_name = split(".", v3)
|
||||||
|
}
|
||||||
|
if v3 != null]]) : {
|
||||||
|
endpoint_attachment = v1.endpoint_attachment
|
||||||
|
domain = length(v1.dns_name) == 1 ? "." : "${join(".", slice(v1.dns_name, 1, length(v1.dns_name)))}."
|
||||||
|
name = length(v1.dns_name) == 1 ? "*." : v1.dns_name[0]
|
||||||
|
}]
|
||||||
|
peered_domains = distinct([for v in local.dns_names : v.domain])
|
||||||
|
private_dns_zones = { for k1, v1 in { for v2 in local.peered_domains :
|
||||||
|
v2 => distinct([for v3 in local.dns_names : v3.name if v3.domain == v2]) } : k1 =>
|
||||||
|
{ for v4 in v1 : "A ${v4}" => {
|
||||||
|
geo_routing = [for v5 in local.dns_names :
|
||||||
|
{
|
||||||
|
location = var.apigee_config.endpoint_attachments[v5.endpoint_attachment].region
|
||||||
|
records = [module.apigee.endpoint_attachment_hosts[v5.endpoint_attachment]]
|
||||||
|
}
|
||||||
|
if v5.domain == k1 && v5.name == v4] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module "private_dns_zones" {
|
||||||
|
for_each = (var.network_config.apigee_vpc == null || var.apigee_config.organization.disable_vpc_peering
|
||||||
|
? {} :
|
||||||
|
local.private_dns_zones)
|
||||||
|
source = "../../../modules/dns"
|
||||||
|
project_id = module.project.project_id
|
||||||
|
name = trimsuffix(replace(each.key, ".", "-"), "-")
|
||||||
|
zone_config = {
|
||||||
|
domain = each.key
|
||||||
|
private = {
|
||||||
|
client_networks = [module.apigee_vpc[0].self_link]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
recordsets = each.value
|
||||||
|
}
|
|
@ -0,0 +1,138 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2024 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const functions = require("@google-cloud/functions-framework");
|
||||||
|
const monitoring = require("@google-cloud/monitoring");
|
||||||
|
const logging = require("@google-cloud/logging");
|
||||||
|
const { LoggingBunyan } = require("@google-cloud/logging-bunyan");
|
||||||
|
const bunyan = require("bunyan");
|
||||||
|
|
||||||
|
const loggingBunyan = new LoggingBunyan();
|
||||||
|
const logger = bunyan.createLogger({
|
||||||
|
name: "instance-monitor",
|
||||||
|
streams: [{ stream: process.stdout, level: "info" }, loggingBunyan.stream("info")],
|
||||||
|
});
|
||||||
|
|
||||||
|
const SEVERITY_THRESHOLD = logging.Severity.warning;
|
||||||
|
|
||||||
|
const METRIC_DESCRIPTION = "Apigee instance health.";
|
||||||
|
const METRIC_DISPLAY_NAME = "Apigee instance health.";
|
||||||
|
const METRIC_TYPE = "custom.googleapis.com/apigee/instance_health";
|
||||||
|
const METRIC_KIND = "GAUGE";
|
||||||
|
const METRIC_VALUE_TYPE = "BOOL";
|
||||||
|
const METRIC_UNIT = "1";
|
||||||
|
const METRIC_LABELS = [
|
||||||
|
{
|
||||||
|
key: "org",
|
||||||
|
valueType: "STRING",
|
||||||
|
description: "The name of the apigee organization.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "instance_id",
|
||||||
|
valueType: "STRING",
|
||||||
|
description: "The ID of the apigee instance.",
|
||||||
|
}
|
||||||
|
];
|
||||||
|
const RESOURCE_TYPE = "global";
|
||||||
|
const METRIC_DESCRIPTOR = {
|
||||||
|
description: METRIC_DESCRIPTION,
|
||||||
|
displayName: METRIC_DISPLAY_NAME,
|
||||||
|
type: METRIC_TYPE,
|
||||||
|
metricKind: METRIC_KIND,
|
||||||
|
valueType: METRIC_VALUE_TYPE,
|
||||||
|
unit: METRIC_UNIT,
|
||||||
|
labels: METRIC_LABELS,
|
||||||
|
};
|
||||||
|
|
||||||
|
const client = new monitoring.MetricServiceClient();
|
||||||
|
|
||||||
|
async function createMetricDescriptor(projectId, metricDescriptor) {
|
||||||
|
const request = {
|
||||||
|
name: client.projectPath(projectId),
|
||||||
|
metricDescriptor: metricDescriptor,
|
||||||
|
};
|
||||||
|
return await client.createMetricDescriptor(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getMetricDescriptor(projectId, metricType) {
|
||||||
|
const request = {
|
||||||
|
name: client.projectMetricDescriptorPath(projectId, metricType),
|
||||||
|
};
|
||||||
|
return await client.getMetricDescriptor(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function writeTimeSeriesData(projectId, metricType, resourceType, value, metricLabels) {
|
||||||
|
const dataPoint = {
|
||||||
|
interval: {
|
||||||
|
endTime: {
|
||||||
|
seconds: Date.now() / 1000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
boolValue: value,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const timeSeriesData = {
|
||||||
|
metric: {
|
||||||
|
type: metricType,
|
||||||
|
labels: metricLabels,
|
||||||
|
},
|
||||||
|
resource: {
|
||||||
|
type: resourceType,
|
||||||
|
labels: {
|
||||||
|
project_id: projectId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
points: [dataPoint],
|
||||||
|
};
|
||||||
|
|
||||||
|
const request = {
|
||||||
|
name: client.projectPath(projectId),
|
||||||
|
timeSeries: [timeSeriesData],
|
||||||
|
};
|
||||||
|
|
||||||
|
return await client.createTimeSeries(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function processEvent(cloudEvent) {
|
||||||
|
const [, projectId, instanceId] = /^organizations\/(.+)\/instances\/(.+)$/g.exec(cloudEvent.resourcename);
|
||||||
|
const severity = logging.Severity[cloudEvent.data.severity.toLowerCase()];
|
||||||
|
const value = severity >= SEVERITY_THRESHOLD;
|
||||||
|
if (!value) {
|
||||||
|
logger.error(`Instance ${instanceId} in ${organization} is down`);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
logger.debug("Checking if metric exists...");
|
||||||
|
const result = await getMetricDescriptor(projectId, METRIC_TYPE);
|
||||||
|
logger.debug("Metric already exists", result);
|
||||||
|
} catch (error) {
|
||||||
|
logger.debug("Metric does not exist. Creating it...");
|
||||||
|
const result = await createMetricDescriptor(projectId, METRIC_DESCRIPTOR);
|
||||||
|
logger.debug("Metric created", result);
|
||||||
|
}
|
||||||
|
logger.debug("Writing data point...");
|
||||||
|
await writeTimeSeriesData(projectId, METRIC_TYPE, RESOURCE_TYPE, value, {
|
||||||
|
org: projectId,
|
||||||
|
instance_id: instanceId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
functions.cloudEvent("writeMetric", async cloudEvent => {
|
||||||
|
logger.debug("Notification received. Let's process it...");
|
||||||
|
processEvent(cloudEvent);
|
||||||
|
logger.debug("Notification processed.");
|
||||||
|
});
|
3271
blueprints/apigee/apigee-x-foundations/functions/instance-monitor/package-lock.json
generated
Normal file
3271
blueprints/apigee/apigee-x-foundations/functions/instance-monitor/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"name": "instance-checker",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@google-cloud/functions-framework": "^3.3.0",
|
||||||
|
"@google-cloud/logging-bunyan": "^5.0.0",
|
||||||
|
"bunyan": "^1.8.15",
|
||||||
|
"@google-cloud/monitoring": "^3.0.5"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2024 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 "random_id" "database_kms" {
|
||||||
|
byte_length = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "random_id" "disks_kms" {
|
||||||
|
for_each = var.apigee_config.instances
|
||||||
|
byte_length = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
module "database_kms" {
|
||||||
|
count = try(var.apigee_config.organization.database_encryption_key, null) == null ? 1 : 0
|
||||||
|
source = "../../../modules/kms"
|
||||||
|
project_id = module.project.project_id
|
||||||
|
keyring = {
|
||||||
|
location = "global"
|
||||||
|
name = "apigee-${random_id.database_kms.hex}"
|
||||||
|
}
|
||||||
|
keys = {
|
||||||
|
database-key = {
|
||||||
|
purpose = "ENCRYPT_DECRYPT"
|
||||||
|
rotation_period = "2592000s"
|
||||||
|
labels = null
|
||||||
|
iam = {
|
||||||
|
"roles/cloudkms.cryptoKeyEncrypterDecrypter" = ["serviceAccount:${module.project.service_accounts.robots.apigee}"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module "disks_kms" {
|
||||||
|
for_each = var.apigee_config.instances
|
||||||
|
source = "../../../modules/kms"
|
||||||
|
project_id = module.project.project_id
|
||||||
|
keyring = {
|
||||||
|
location = each.key
|
||||||
|
name = "apigee-${each.key}-${random_id.disks_kms[each.key].hex}"
|
||||||
|
}
|
||||||
|
keys = {
|
||||||
|
disk-key = {
|
||||||
|
purpose = "ENCRYPT_DECRYPT"
|
||||||
|
rotation_period = "2592000s"
|
||||||
|
labels = null
|
||||||
|
iam = {
|
||||||
|
"roles/cloudkms.cryptoKeyEncrypterDecrypter" = ["serviceAccount:${module.project.service_accounts.robots.apigee}"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2024 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module "project" {
|
||||||
|
source = "../../../modules/project"
|
||||||
|
billing_account = var.project_config.billing_account_id
|
||||||
|
compute_metadata = var.project_config.compute_metadata
|
||||||
|
custom_roles = var.project_config.custom_roles
|
||||||
|
default_service_account = var.project_config.default_service_account
|
||||||
|
iam = var.project_config.iam
|
||||||
|
iam_bindings = var.project_config.iam_bindings
|
||||||
|
iam_bindings_additive = var.project_config.iam_bindings_additive
|
||||||
|
labels = var.project_config.labels
|
||||||
|
lien_reason = var.project_config.lien_reason
|
||||||
|
logging_data_access = var.project_config.logging_data_access
|
||||||
|
logging_exclusions = var.project_config.log_exclusions
|
||||||
|
logging_sinks = var.project_config.logging_sinks
|
||||||
|
metric_scopes = var.project_config.metric_scopes
|
||||||
|
name = var.project_config.name
|
||||||
|
org_policies = var.project_config.org_policies
|
||||||
|
parent = var.project_config.parent
|
||||||
|
prefix = var.project_config.prefix
|
||||||
|
services = distinct(concat(var.project_config.services, [
|
||||||
|
"apigee.googleapis.com",
|
||||||
|
"cloudkms.googleapis.com",
|
||||||
|
"compute.googleapis.com",
|
||||||
|
"eventarc.googleapis.com",
|
||||||
|
"dns.googleapis.com",
|
||||||
|
"iam.googleapis.com",
|
||||||
|
"servicenetworking.googleapis.com",
|
||||||
|
], var.enable_monitoring ? [
|
||||||
|
"cloudbuild.googleapis.com",
|
||||||
|
"cloudfunctions.googleapis.com",
|
||||||
|
"logging.googleapis.com",
|
||||||
|
"monitoring.googleapis.com",
|
||||||
|
"pubsub.googleapis.com",
|
||||||
|
"run.googleapis.com"
|
||||||
|
] : []))
|
||||||
|
|
||||||
|
shared_vpc_service_config = var.project_config.shared_vpc_service_config
|
||||||
|
skip_delete = var.project_config.skip_delete
|
||||||
|
tag_bindings = var.project_config.tag_bindings
|
||||||
|
}
|
||||||
|
|
||||||
|
module "shared_vpc" {
|
||||||
|
count = var.network_config.shared_vpc == null ? 0 : 1
|
||||||
|
source = "../../../modules/net-vpc"
|
||||||
|
project_id = var.project_config.shared_vpc_service_config.host_project
|
||||||
|
name = var.network_config.shared_vpc.name
|
||||||
|
vpc_create = false
|
||||||
|
}
|
||||||
|
|
||||||
|
module "apigee_vpc" {
|
||||||
|
count = var.network_config.apigee_vpc == null ? 0 : 1
|
||||||
|
source = "../../../modules/net-vpc"
|
||||||
|
project_id = module.project.project_id
|
||||||
|
name = coalesce(var.network_config.apigee_vpc.name, "apigee-vpc")
|
||||||
|
vpc_create = var.network_config.apigee_vpc.auto_create
|
||||||
|
psa_configs = [{
|
||||||
|
ranges = merge(flatten([for k, v in var.apigee_config.instances : merge(
|
||||||
|
v.runtime_ip_cidr_range == null ? {} : { "apigee-22-${k}" = v.runtime_ip_cidr_range },
|
||||||
|
v.troubleshooting_ip_cidr_range == null ? {} : { "apigee-28-${k}" = v.troubleshooting_ip_cidr_range }
|
||||||
|
)])...)
|
||||||
|
export_routes = true
|
||||||
|
import_routes = false
|
||||||
|
peered_domains = local.peered_domains
|
||||||
|
}]
|
||||||
|
subnets = [for k, v in var.network_config.apigee_vpc.subnets :
|
||||||
|
{
|
||||||
|
name = coalesce(v.name, "subnet-${k}")
|
||||||
|
region = k
|
||||||
|
ip_cidr_range = v.ip_cidr_range
|
||||||
|
description = "Subnet in ${k} region"
|
||||||
|
}
|
||||||
|
if v.ip_cidr_range != null && (var.int_cross_region_lb_config != null || nonsensitive(var.int_lb_config != null))]
|
||||||
|
subnets_proxy_only = [for k, v in var.network_config.apigee_vpc.subnets_proxy_only :
|
||||||
|
{
|
||||||
|
name = coalesce(v.name, "subnet-proxy-only-${k}")
|
||||||
|
region = k
|
||||||
|
ip_cidr_range = v.ip_cidr_range
|
||||||
|
description = "Proxy-only subnet in ${k} region"
|
||||||
|
global = var.int_cross_region_lb_config != null
|
||||||
|
}
|
||||||
|
if v.ip_cidr_range != null && (var.int_cross_region_lb_config != null || nonsensitive(var.int_lb_config != null))]
|
||||||
|
subnets_psc = [for k, v in var.network_config.apigee_vpc.subnets_psc :
|
||||||
|
{
|
||||||
|
name = coalesce(v.name, "subnet-psc-${k}")
|
||||||
|
region = k
|
||||||
|
ip_cidr_range = v.ip_cidr_range
|
||||||
|
description = "PSC Subnet in ${k} region"
|
||||||
|
global = var.int_cross_region_lb_config != null
|
||||||
|
}
|
||||||
|
if v.ip_cidr_range != null]
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2024 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module "instance_monitor_function" {
|
||||||
|
count = var.enable_monitoring && length(var.apigee_config.instances) > 0 ? 1 : 0
|
||||||
|
source = "../../../modules/cloud-function-v2"
|
||||||
|
project_id = module.project.project_id
|
||||||
|
name = "instance-monitor"
|
||||||
|
bucket_name = module.project.project_id
|
||||||
|
bucket_config = {
|
||||||
|
}
|
||||||
|
bundle_config = {
|
||||||
|
source_dir = "${path.module}/functions/instance-monitor"
|
||||||
|
output_path = "bundle.zip"
|
||||||
|
}
|
||||||
|
function_config = {
|
||||||
|
entry_point = "writeMetric"
|
||||||
|
runtime = "nodejs20"
|
||||||
|
timeout = 180
|
||||||
|
}
|
||||||
|
trigger_config = {
|
||||||
|
event_type = "google.cloud.audit.log.v1.written"
|
||||||
|
region = "global"
|
||||||
|
event_filters = [
|
||||||
|
{
|
||||||
|
attribute = "serviceName"
|
||||||
|
value = "apigee.googleapis.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
attribute = "methodName"
|
||||||
|
value = "google.cloud.apigee.v1.RuntimeService.ReportInstanceStatus"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
service_account_create = true
|
||||||
|
retry_policy = "RETRY_POLICY_DO_NOT_RETRY"
|
||||||
|
}
|
||||||
|
region = var.apigee_config.organization.analytics_region
|
||||||
|
service_account_create = true
|
||||||
|
}
|
|
@ -0,0 +1,248 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2024 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 {
|
||||||
|
preconfigured_waf_rules = { for k, v in try(var.ext_lb_config.security_policy.preconfigured_waf_rules, {}) : k =>
|
||||||
|
merge(v.sensitivity == null ? {} : {
|
||||||
|
sensitivity = v.sensitivity
|
||||||
|
},
|
||||||
|
length(v.opt_in_rule_ids) > 0 ? {
|
||||||
|
opt_in_rule_ids = v.opt_in_rule_ids
|
||||||
|
} : {},
|
||||||
|
length(v.opt_out_rule_ids) > 0 ? {
|
||||||
|
opt_out_rule_ids = v.opt_out_rule_ids
|
||||||
|
} : {})
|
||||||
|
}
|
||||||
|
network = try(module.shared_vpc[0].id, module.apigee_vpc[0].id)
|
||||||
|
neg_subnets = (var.network_config.shared_vpc == null ?
|
||||||
|
(try(var.network_config.apigee_vpc.auto_create, false) ?
|
||||||
|
{ for k, v in module.apigee_vpc[0].subnets_psc : v.region => v.id } :
|
||||||
|
{ for k, v in var.network_config.apigee_vpc.subnets_psc : v => v.id }) :
|
||||||
|
var.network_config.shared_vpc.subnets_psc
|
||||||
|
)
|
||||||
|
ilb_subnets = (var.network_config.shared_vpc == null ?
|
||||||
|
(try(var.network_config.apigee_vpc.auto_create, false) ?
|
||||||
|
{ for k, v in module.apigee_vpc[0].subnets : v.region => v.id } :
|
||||||
|
{ for k, v in var.network_config.apigee_vpc.subnets : v => v.id }) :
|
||||||
|
var.network_config.shared_vpc.subnets
|
||||||
|
)
|
||||||
|
ext_instances = var.ext_lb_config == null ? {} : { for k, v in local.neg_subnets : k => module.apigee.instances[k] }
|
||||||
|
int_instances = var.int_lb_config == null ? {} : { for k, v in local.ilb_subnets : k => module.apigee.instances[k] }
|
||||||
|
int_cross_region_instances = var.int_cross_region_lb_config == null ? {} : { for k, v in local.ilb_subnets : k => module.apigee.instances[k] }
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "google_compute_region_network_endpoint_group" "psc_negs" {
|
||||||
|
for_each = local.neg_subnets
|
||||||
|
project = module.project.project_id
|
||||||
|
region = each.key
|
||||||
|
name = "apigee-${each.key}"
|
||||||
|
network_endpoint_type = "PRIVATE_SERVICE_CONNECT"
|
||||||
|
psc_target_service = module.apigee.instances[each.key].service_attachment
|
||||||
|
network = local.network
|
||||||
|
subnetwork = each.value
|
||||||
|
}
|
||||||
|
|
||||||
|
module "ext_lb" {
|
||||||
|
count = length(local.ext_instances) > 0 ? 1 : 0
|
||||||
|
source = "../../../modules/net-lb-app-ext"
|
||||||
|
name = "ext-lb"
|
||||||
|
project_id = module.project.project_id
|
||||||
|
protocol = "HTTPS"
|
||||||
|
use_classic_version = false
|
||||||
|
backend_service_configs = {
|
||||||
|
default = {
|
||||||
|
backends = [for k, v in local.ext_instances : { backend = google_compute_region_network_endpoint_group.psc_negs[k].id }]
|
||||||
|
protocol = "HTTPS"
|
||||||
|
health_checks = []
|
||||||
|
outlier_detection = var.ext_lb_config.outlier_detection
|
||||||
|
security_policy = try(google_compute_security_policy.policy[0].name, null)
|
||||||
|
log_sample_rate = var.ext_lb_config.log_sample_rate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
health_check_configs = {
|
||||||
|
default = {
|
||||||
|
https = { port_specification = "USE_SERVING_PORT" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ssl_certificates = var.ext_lb_config.ssl_certificates
|
||||||
|
}
|
||||||
|
|
||||||
|
module "int_lb" {
|
||||||
|
for_each = local.int_instances
|
||||||
|
source = "../../../modules/net-lb-app-int"
|
||||||
|
name = "${each.key}-int-lb"
|
||||||
|
project_id = module.project.project_id
|
||||||
|
region = each.key
|
||||||
|
protocol = "HTTPS"
|
||||||
|
backend_service_configs = {
|
||||||
|
default = {
|
||||||
|
backends = [{
|
||||||
|
group = google_compute_region_network_endpoint_group.psc_negs[each.key].id
|
||||||
|
}]
|
||||||
|
outlier_detection = var.int_lb_config.outlier_detection
|
||||||
|
health_checks = []
|
||||||
|
log_sample_rate = var.int_lb_config.log_sample_rate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ssl_certificates = var.int_lb_config.ssl_certificates
|
||||||
|
vpc_config = {
|
||||||
|
network = local.network
|
||||||
|
subnetwork = local.ilb_subnets[each.key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module "int_cross_region_lb" {
|
||||||
|
count = length(local.int_cross_region_instances) > 0 ? 1 : 0
|
||||||
|
source = "../../../modules/net-lb-app-int-cross-region"
|
||||||
|
name = "int-cross-region-lb"
|
||||||
|
project_id = module.project.project_id
|
||||||
|
protocol = "HTTPS"
|
||||||
|
backend_service_configs = {
|
||||||
|
default = {
|
||||||
|
backends = [for k, v in google_compute_region_network_endpoint_group.psc_negs : {
|
||||||
|
group = v.id
|
||||||
|
}]
|
||||||
|
outlier_detection = var.int_cross_region_lb_config.outlier_detection
|
||||||
|
health_checks = []
|
||||||
|
log_sample_rate = var.int_cross_region_lb_config.log_sample_rate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
https_proxy_config = {
|
||||||
|
certificate_manager_certificates = var.int_cross_region_lb_config.certificate_manager_certificates
|
||||||
|
}
|
||||||
|
vpc_config = {
|
||||||
|
network = local.network
|
||||||
|
subnetworks = local.ilb_subnets
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "google_compute_security_policy" "policy" {
|
||||||
|
provider = google-beta
|
||||||
|
count = try(var.ext_lb_config.security_policy, null) == null ? 0 : 1
|
||||||
|
name = "cloud-armor-security-policy"
|
||||||
|
description = "Cloud Armor Security Policy"
|
||||||
|
project = module.project.project_id
|
||||||
|
dynamic "advanced_options_config" {
|
||||||
|
for_each = try(var.ext_lb_config, null) == null ? [] : [""]
|
||||||
|
content {
|
||||||
|
json_parsing = try(var.ext_lb_config.security_policy.adaptive_protection_config.json_parsing.enable, false) ? "DISABLED" : "STANDARD"
|
||||||
|
dynamic "json_custom_config" {
|
||||||
|
for_each = try(var.ext_lb_config.security_policy.adaptive_protection_config.json_parsing.content_types, null) == null ? [] : [""]
|
||||||
|
content {
|
||||||
|
content_types = var.ext_lb_config.security_policy.adaptive_protection_config.json_parsing.content_types
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log_level = var.ext_lb_config.security_policy.advanced_options_config.log_level
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dynamic "adaptive_protection_config" {
|
||||||
|
for_each = try(var.ext_lb_config.security_policy.adaptive_protection_config, null) == null ? [] : [""]
|
||||||
|
content {
|
||||||
|
dynamic "layer_7_ddos_defense_config" {
|
||||||
|
for_each = try(var.ext_lb_config.security_policy.adaptive_protection_config.layer_7_ddos_defense_config, null) == null ? [] : [""]
|
||||||
|
content {
|
||||||
|
enable = var.ext_lb_config.security_policy.adaptive_protection_config.layer_7_ddos_defense_config.enable
|
||||||
|
rule_visibility = var.ext_lb_config.security_policy.adaptive_protection_config.layer_7_ddos_defense_config.rule_visibility
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dynamic "auto_deploy_config" {
|
||||||
|
for_each = try(var.int_lb_config.security_policy.adaptive_protection_config.auto_deploy_config, null) == null ? [] : [""]
|
||||||
|
content {
|
||||||
|
load_threshold = var.ext_lb_config.security_policy.adaptive_protection_config.auto_deploy_config.load_threshold
|
||||||
|
confidence_threshold = var.ext_lb_config.security_policy.adaptive_protection_config.auto_deploy_config.confidence_threshold
|
||||||
|
impacted_baseline_threshold = var.ext_lb_config.security_policy.adaptive_protection_config.auto_deploy_config.impacted_baseline_threshold
|
||||||
|
expiration_sec = var.ext_lb_config.security_policy.adaptive_protection_config.auto_deploy_config.expiration_sec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type = "CLOUD_ARMOR"
|
||||||
|
dynamic "rule" {
|
||||||
|
for_each = try(var.ext_lb_config.security_policy.rate_limit_threshold, null) == null ? [] : [""]
|
||||||
|
content {
|
||||||
|
action = "throttle"
|
||||||
|
priority = 3000
|
||||||
|
rate_limit_options {
|
||||||
|
enforce_on_key = "ALL"
|
||||||
|
conform_action = "allow"
|
||||||
|
exceed_action = "deny(429)"
|
||||||
|
rate_limit_threshold {
|
||||||
|
count = var.ext_lb_config.security_policy.rate_limit_threshold.count
|
||||||
|
interval_sec = var.ext_lb_config.security_policy.rate_limit_threshold.interval_sec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match {
|
||||||
|
versioned_expr = "SRC_IPS_V1"
|
||||||
|
config {
|
||||||
|
src_ip_ranges = ["*"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
description = "Rate limit all user IPs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dynamic "rule" {
|
||||||
|
for_each = try(length(var.ext_lb_config.security_policy.forbidden_src_ip_ranges), 0) > 0 ? [""] : []
|
||||||
|
content {
|
||||||
|
action = "deny(403)"
|
||||||
|
priority = 5000
|
||||||
|
match {
|
||||||
|
versioned_expr = "SRC_IPS_V1"
|
||||||
|
config {
|
||||||
|
src_ip_ranges = var.ext_lb_config.security_policy.forbidden_src_ip_ranges
|
||||||
|
}
|
||||||
|
}
|
||||||
|
description = "Deny access to IPs in specific ranges"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dynamic "rule" {
|
||||||
|
for_each = try(length(var.ext_lb_config.security_policy.forbidden_regions), 0) > 0 ? [""] : []
|
||||||
|
content {
|
||||||
|
action = "deny(403)"
|
||||||
|
priority = 7000
|
||||||
|
match {
|
||||||
|
expr {
|
||||||
|
expression = "origin.region_code.matches(\"^${join("|", var.ext_lb_config.security_policy.forbidden_regions)}$\")"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
description = "Block users from forbidden regions"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dynamic "rule" {
|
||||||
|
for_each = local.preconfigured_waf_rules
|
||||||
|
content {
|
||||||
|
action = "deny(403)"
|
||||||
|
priority = 10000 + index(keys(var.ext_lb_config.security_policy.preconfigured_waf_rules), rule.key) * 1000
|
||||||
|
match {
|
||||||
|
expr {
|
||||||
|
expression = "evaluatePreconfiguredWaf(\"${rule.key}\"${length(rule.value) > 0 ? join("", [",", jsonencode(rule.value)]) : ""})"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
description = "Preconfigured WAF rule (${rule.key})"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rule {
|
||||||
|
action = "allow"
|
||||||
|
priority = 2147483647
|
||||||
|
match {
|
||||||
|
versioned_expr = "SRC_IPS_V1"
|
||||||
|
config {
|
||||||
|
src_ip_ranges = ["*"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
description = "default rule"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2024 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 attachment hosts."
|
||||||
|
value = module.apigee.endpoint_attachment_hosts
|
||||||
|
}
|
||||||
|
|
||||||
|
output "ext_lb_ip_address" {
|
||||||
|
description = "External IP address."
|
||||||
|
value = var.ext_lb_config != null && length(local.ext_instances) > 0 ? module.ext_lb[0].address : null
|
||||||
|
}
|
||||||
|
|
||||||
|
output "instance_service_attachments" {
|
||||||
|
description = "Instance service attachments."
|
||||||
|
value = { for k, v in module.apigee.instances : k => v.service_attachment }
|
||||||
|
}
|
||||||
|
|
||||||
|
output "int_cross_region_lb_ip_addresses" {
|
||||||
|
description = "Internal IP addresses."
|
||||||
|
value = var.int_cross_region_lb_config != null && length(local.int_cross_region_instances) > 0 ? module.int_cross_region_lb[0].addresses : null
|
||||||
|
}
|
||||||
|
|
||||||
|
output "int_lb_ip_addresses" {
|
||||||
|
description = "Internal IP addresses."
|
||||||
|
value = var.int_lb_config != null && length(local.int_instances) > 0 ? { for k, v in module.int_lb : k => v.address } : null
|
||||||
|
}
|
||||||
|
|
||||||
|
output "project_id" {
|
||||||
|
description = "Project."
|
||||||
|
value = module.project.project_id
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,360 @@
|
||||||
|
/**
|
||||||
|
* 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 "apigee_config" {
|
||||||
|
description = "Apigee configuration."
|
||||||
|
type = object({
|
||||||
|
addons_config = optional(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)
|
||||||
|
}))
|
||||||
|
organization = object({
|
||||||
|
display_name = optional(string)
|
||||||
|
description = optional(string, "Terraform-managed")
|
||||||
|
billing_type = optional(string)
|
||||||
|
database_encryption_key = optional(string)
|
||||||
|
analytics_region = optional(string, "europe-west1")
|
||||||
|
retention = optional(string)
|
||||||
|
disable_vpc_peering = optional(bool, false)
|
||||||
|
})
|
||||||
|
envgroups = optional(map(list(string)), {})
|
||||||
|
environments = optional(map(object({
|
||||||
|
description = optional(string)
|
||||||
|
display_name = optional(string)
|
||||||
|
envgroups = optional(list(string), [])
|
||||||
|
iam = optional(map(list(string)), {})
|
||||||
|
iam_bindings = optional(map(object({
|
||||||
|
role = string
|
||||||
|
members = list(string)
|
||||||
|
condition = optional(object({
|
||||||
|
expression = string
|
||||||
|
title = string
|
||||||
|
description = optional(string)
|
||||||
|
}))
|
||||||
|
})), {})
|
||||||
|
iam_bindings_additive = optional(map(object({
|
||||||
|
role = string
|
||||||
|
member = string
|
||||||
|
condition = optional(object({
|
||||||
|
expression = string
|
||||||
|
title = string
|
||||||
|
description = optional(string)
|
||||||
|
}))
|
||||||
|
})), {})
|
||||||
|
node_config = optional(object({
|
||||||
|
min_node_count = optional(number)
|
||||||
|
max_node_count = optional(number)
|
||||||
|
}), {})
|
||||||
|
type = optional(string)
|
||||||
|
})), {})
|
||||||
|
instances = optional(map(object({
|
||||||
|
disk_encryption_key = optional(string)
|
||||||
|
environments = optional(list(string), [])
|
||||||
|
external = optional(bool, true)
|
||||||
|
runtime_ip_cidr_range = optional(string)
|
||||||
|
troubleshooting_ip_cidr_range = optional(string)
|
||||||
|
})), {})
|
||||||
|
endpoint_attachments = optional(map(object({
|
||||||
|
region = string
|
||||||
|
service_attachment = string
|
||||||
|
dns_names = optional(list(string), [])
|
||||||
|
})), {})
|
||||||
|
})
|
||||||
|
validation {
|
||||||
|
condition = (!var.apigee_config.organization.disable_vpc_peering ||
|
||||||
|
alltrue([for k, v in var.apigee_config.endpoint_attachments : length(v.dns_names) == 0]))
|
||||||
|
error_message = "If disable_vpc_peering is true for the organization, DNS names cannot be used for endpoint attachments."
|
||||||
|
}
|
||||||
|
nullable = false
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "enable_monitoring" {
|
||||||
|
description = "Boolean flag indicating whether an custom metric to monitor instances should be created in Cloud monitoring."
|
||||||
|
type = bool
|
||||||
|
default = false
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ext_lb_config" {
|
||||||
|
description = "External application load balancer configuration."
|
||||||
|
type = object({
|
||||||
|
log_sample_rate = optional(number)
|
||||||
|
outlier_detection = optional(object({
|
||||||
|
consecutive_errors = optional(number)
|
||||||
|
consecutive_gateway_failure = optional(number)
|
||||||
|
enforcing_consecutive_errors = optional(number)
|
||||||
|
enforcing_consecutive_gateway_failure = optional(number)
|
||||||
|
enforcing_success_rate = optional(number)
|
||||||
|
max_ejection_percent = optional(number)
|
||||||
|
success_rate_minimum_hosts = optional(number)
|
||||||
|
success_rate_request_volume = optional(number)
|
||||||
|
success_rate_stdev_factor = optional(number)
|
||||||
|
base_ejection_time = optional(object({
|
||||||
|
seconds = number
|
||||||
|
nanos = optional(number)
|
||||||
|
}))
|
||||||
|
interval = optional(object({
|
||||||
|
seconds = number
|
||||||
|
nanos = optional(number)
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
security_policy = optional(object({
|
||||||
|
advanced_options_config = optional(object({
|
||||||
|
json_parsing = optional(object({
|
||||||
|
enable = optional(bool, false)
|
||||||
|
content_types = optional(list(string))
|
||||||
|
}))
|
||||||
|
log_level = optional(string)
|
||||||
|
}))
|
||||||
|
adaptive_protection_config = optional(object({
|
||||||
|
layer_7_ddos_defense_config = optional(object({
|
||||||
|
enable = optional(bool, false)
|
||||||
|
rule_visibility = optional(string)
|
||||||
|
}))
|
||||||
|
auto_deploy_config = optional(object({
|
||||||
|
load_threshold = optional(number)
|
||||||
|
confidence_threshold = optional(number)
|
||||||
|
impacted_baseline_threshold = optional(number)
|
||||||
|
expiration_sec = optional(number)
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
rate_limit_threshold = optional(object({
|
||||||
|
count = number
|
||||||
|
interval_sec = number
|
||||||
|
}))
|
||||||
|
forbidden_src_ip_ranges = optional(list(string), [])
|
||||||
|
forbidden_regions = optional(list(string), [])
|
||||||
|
preconfigured_waf_rules = optional(map(object({
|
||||||
|
sensitivity = optional(number)
|
||||||
|
opt_in_rule_ids = optional(list(string), [])
|
||||||
|
opt_out_rule_ids = optional(list(string), [])
|
||||||
|
})))
|
||||||
|
}))
|
||||||
|
ssl_certificates = object({
|
||||||
|
certificate_ids = optional(list(string), [])
|
||||||
|
create_configs = optional(map(object({
|
||||||
|
certificate = string
|
||||||
|
private_key = string
|
||||||
|
})), {})
|
||||||
|
managed_configs = optional(map(object({
|
||||||
|
domains = list(string)
|
||||||
|
description = optional(string)
|
||||||
|
})), {})
|
||||||
|
self_signed_configs = optional(list(string), null)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
default = null
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "int_cross_region_lb_config" {
|
||||||
|
description = "Internal application load balancer configuration."
|
||||||
|
type = object({
|
||||||
|
log_sample_rate = optional(number)
|
||||||
|
outlier_detection = optional(object({
|
||||||
|
consecutive_errors = optional(number)
|
||||||
|
consecutive_gateway_failure = optional(number)
|
||||||
|
enforcing_consecutive_errors = optional(number)
|
||||||
|
enforcing_consecutive_gateway_failure = optional(number)
|
||||||
|
enforcing_success_rate = optional(number)
|
||||||
|
max_ejection_percent = optional(number)
|
||||||
|
success_rate_minimum_hosts = optional(number)
|
||||||
|
success_rate_request_volume = optional(number)
|
||||||
|
success_rate_stdev_factor = optional(number)
|
||||||
|
base_ejection_time = optional(object({
|
||||||
|
seconds = number
|
||||||
|
nanos = optional(number)
|
||||||
|
}))
|
||||||
|
interval = optional(object({
|
||||||
|
seconds = number
|
||||||
|
nanos = optional(number)
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
certificate_manager_certificates = optional(list(string))
|
||||||
|
})
|
||||||
|
default = null
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "int_lb_config" {
|
||||||
|
description = "Internal application load balancer configuration."
|
||||||
|
type = object({
|
||||||
|
log_sample_rate = optional(number)
|
||||||
|
outlier_detection = optional(object({
|
||||||
|
consecutive_errors = optional(number)
|
||||||
|
consecutive_gateway_failure = optional(number)
|
||||||
|
enforcing_consecutive_errors = optional(number)
|
||||||
|
enforcing_consecutive_gateway_failure = optional(number)
|
||||||
|
enforcing_success_rate = optional(number)
|
||||||
|
max_ejection_percent = optional(number)
|
||||||
|
success_rate_minimum_hosts = optional(number)
|
||||||
|
success_rate_request_volume = optional(number)
|
||||||
|
success_rate_stdev_factor = optional(number)
|
||||||
|
base_ejection_time = optional(object({
|
||||||
|
seconds = number
|
||||||
|
nanos = optional(number)
|
||||||
|
}))
|
||||||
|
interval = optional(object({
|
||||||
|
seconds = number
|
||||||
|
nanos = optional(number)
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
ssl_certificates = object({
|
||||||
|
certificate_ids = optional(list(string), [])
|
||||||
|
create_configs = optional(map(object({
|
||||||
|
certificate = string
|
||||||
|
private_key = string
|
||||||
|
})), {})
|
||||||
|
self_signed_configs = optional(list(string), [])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
default = null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
variable "network_config" {
|
||||||
|
description = "Network configuration."
|
||||||
|
type = object({
|
||||||
|
shared_vpc = optional(object({
|
||||||
|
name = string
|
||||||
|
subnets = map(string)
|
||||||
|
subnets_psc = map(string)
|
||||||
|
}))
|
||||||
|
apigee_vpc = optional(object({
|
||||||
|
name = optional(string)
|
||||||
|
auto_create = optional(bool, true)
|
||||||
|
subnets = optional(map(object({
|
||||||
|
id = optional(string)
|
||||||
|
name = optional(string)
|
||||||
|
ip_cidr_range = optional(string)
|
||||||
|
})), {})
|
||||||
|
subnets_proxy_only = optional(map(object({
|
||||||
|
name = optional(string)
|
||||||
|
ip_cidr_range = string
|
||||||
|
})), {})
|
||||||
|
subnets_psc = optional(map(object({
|
||||||
|
id = optional(string)
|
||||||
|
name = optional(string)
|
||||||
|
ip_cidr_range = optional(string)
|
||||||
|
})), {})
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
nullable = false
|
||||||
|
default = {}
|
||||||
|
validation {
|
||||||
|
condition = var.network_config.shared_vpc != null || var.network_config.apigee_vpc != null
|
||||||
|
error_message = "Shared VPC and/or local VPC details need to be provided."
|
||||||
|
}
|
||||||
|
validation {
|
||||||
|
condition = alltrue([for k, v in try(var.network_config.apigee_vpc.subnets, {}) : (v.id != null || v.ip_cidr_range != null) && !(v.id != null && v.ip_cidr_range != null)])
|
||||||
|
error_message = "An IP CIDR range and id cannot be specified at the same time for a subnet."
|
||||||
|
}
|
||||||
|
validation {
|
||||||
|
condition = alltrue([for k, v in try(var.network_config.apigee_vpc.subnets_psc, {}) : (v.id != null || v.ip_cidr_range != null) && !(v.id != null && v.ip_cidr_range != null)])
|
||||||
|
error_message = "An IP CIDR range and id cannot be specified at the same time for a PSC subnet."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "project_config" {
|
||||||
|
description = "Project configuration."
|
||||||
|
type = object({
|
||||||
|
billing_account_id = optional(string)
|
||||||
|
compute_metadata = optional(map(string), {})
|
||||||
|
contacts = optional(map(list(string)), {})
|
||||||
|
custom_roles = optional(map(list(string)), {})
|
||||||
|
default_service_account = optional(string, "keep")
|
||||||
|
descriptive_name = optional(string)
|
||||||
|
iam = optional(map(list(string)), {})
|
||||||
|
group_iam = optional(map(list(string)), {})
|
||||||
|
iam_bindings = optional(map(object({
|
||||||
|
role = string
|
||||||
|
members = list(string)
|
||||||
|
condition = optional(object({
|
||||||
|
expression = string
|
||||||
|
title = string
|
||||||
|
description = optional(string)
|
||||||
|
}))
|
||||||
|
})), {})
|
||||||
|
iam_bindings_additive = optional(map(object({
|
||||||
|
role = string
|
||||||
|
member = string
|
||||||
|
condition = optional(object({
|
||||||
|
expression = string
|
||||||
|
title = string
|
||||||
|
description = optional(string)
|
||||||
|
}))
|
||||||
|
})), {})
|
||||||
|
labels = optional(map(string), {})
|
||||||
|
lien_reason = optional(string)
|
||||||
|
logging_data_access = optional(map(map(list(string))), {})
|
||||||
|
log_exclusions = optional(map(string), {})
|
||||||
|
logging_sinks = optional(map(object({
|
||||||
|
bq_partitioned_table = optional(bool)
|
||||||
|
description = optional(string)
|
||||||
|
destination = string
|
||||||
|
disabled = optional(bool, false)
|
||||||
|
exclusions = optional(map(string), {})
|
||||||
|
filter = string
|
||||||
|
iam = optional(bool, true)
|
||||||
|
type = string
|
||||||
|
unique_writer = optional(bool, true)
|
||||||
|
})), {})
|
||||||
|
metric_scopes = optional(list(string), [])
|
||||||
|
name = string
|
||||||
|
org_policies = optional(map(object({
|
||||||
|
inherit_from_parent = optional(bool) # for list policies only.
|
||||||
|
reset = optional(bool)
|
||||||
|
rules = optional(list(object({
|
||||||
|
allow = optional(object({
|
||||||
|
all = optional(bool)
|
||||||
|
values = optional(list(string))
|
||||||
|
}))
|
||||||
|
deny = optional(object({
|
||||||
|
all = optional(bool)
|
||||||
|
values = optional(list(string))
|
||||||
|
}))
|
||||||
|
enforce = optional(bool) # for boolean policies only.
|
||||||
|
condition = optional(object({
|
||||||
|
description = optional(string)
|
||||||
|
expression = optional(string)
|
||||||
|
location = optional(string)
|
||||||
|
title = optional(string)
|
||||||
|
}), {})
|
||||||
|
})), [])
|
||||||
|
})), {})
|
||||||
|
parent = optional(string)
|
||||||
|
prefix = optional(string)
|
||||||
|
project_create = optional(bool, true)
|
||||||
|
vpc_sc = optional(object({
|
||||||
|
perimeter_name = string
|
||||||
|
perimeter_bridges = optional(list(string), [])
|
||||||
|
is_dry_run = optional(bool, false)
|
||||||
|
}))
|
||||||
|
services = optional(list(string), [])
|
||||||
|
shared_vpc_host_config = optional(object({
|
||||||
|
enabled = bool
|
||||||
|
service_projects = optional(list(string), [])
|
||||||
|
}))
|
||||||
|
shared_vpc_service_config = optional(object({
|
||||||
|
host_project = string
|
||||||
|
service_identity_iam = optional(map(list(string)), {})
|
||||||
|
service_iam_grants = optional(list(string), [])
|
||||||
|
}))
|
||||||
|
skip_delete = optional(bool, false)
|
||||||
|
tag_bindings = optional(map(string))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Internal Application Load Balancer Module
|
# Cross-region Internal Application Load Balancer Module
|
||||||
|
|
||||||
This module allows managing Cross-regional Internal HTTP/HTTPS Load Balancers (L7 ILBs). It's designed to expose the full configuration of the underlying resources, and to facilitate common usage patterns by providing sensible defaults, and optionally managing prerequisite resources like health checks, instance groups, etc.
|
This module allows managing Cross-regional Internal HTTP/HTTPS Load Balancers (L7 ILBs). It's designed to expose the full configuration of the underlying resources, and to facilitate common usage patterns by providing sensible defaults, and optionally managing prerequisite resources like health checks, instance groups, etc.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue