Merge pull request #1175 from juliodiez/serverless-program

Serverless networking program
This commit is contained in:
Julio Diez 2023-02-25 11:15:11 +01:00 committed by GitHub
commit 463dc41ede
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 1090 additions and 0 deletions

View File

@ -0,0 +1,287 @@
# Cloud Run Corporate
## Introduction
This blueprint contains all the necessary Terraform modules to build and __privately__ expose a Cloud Run service in a variety of use cases.
The content of this blueprint corresponds to the chapter '_Developing an enterprise application - The corporate environment_' of the __Serverless Networking Guide__ (to be released soon). This guide is an easy to follow introduction to Cloud Run, where a couple of friendly characters will guide you from the basics to more advanced topics with a very practical approach and in record time! The code here complements this learning and allows you to test the scenarios presented and your knowledge.
If you are interested in following this guide, take a look to the chapters' blueprints:
* [My serverless "Hello, World! - Exploring Cloud Run](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/tree/master/blueprints/serverless/cloud-run-explore)
* [Developing an enterprise application - The corporate environment](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/tree/master/blueprints/serverless/cloud-run-corporate)
## Architecture
This blueprint creates multiple architectures depending on the use case. Some may have one or two projecs while others may have three or more. Some use [Private Service Connect (PSC)](https://cloud.google.com/vpc/docs/private-service-connect) to access Google APIs, and others a [Layer 7 Internal Load Balancer](https://cloud.google.com/load-balancing/docs/l7-internal). Even security plays a role and [VPC Service Controls (VPC SC)](https://cloud.google.com/vpc-service-controls) is introduced.
## Prerequisites
Depending on the use case, you will need one or more projects with [billing enabled](https://cloud.google.com/billing/docs/how-to/modify-project) and a user with the “Project owner” [IAM](https://cloud.google.com/iam) role on those projects. You can use existing projects or let the blueprint creates them for you but in that case you will need to add extra information for each project. E.g.:
```tfvars
# Create the main project
prj_main_create = {
billing_account_id = "ABCDE-12345-ABCDE"
parent = "organizations/0123456789"
}
```
Below it is explained how to set this information.
## Spinning up the architecture
### General steps
1. Clone the repo to your local machine or Cloud Shell:
```bash
git clone https://github.com/GoogleCloudPlatform/cloud-foundation-fabric
```
2. Change to the directory of the blueprint:
```bash
cd cloud-foundation-fabric/blueprints/serverless/cloud-run-corporate
```
You should see this README and some terraform files.
3. To deploy a specific use case, you will need to create a file in this directory called `terraform.tfvars` and follow the corresponding instructions to set variables. Sometimes values that are meant to be substituted will be shown inside brackets but you need to omit these brackets. E.g.:
```tfvars
project_id = "[your-project_id]"
```
may become
```tfvars
project_id = "spiritual-hour-331417"
```
Use cases are self-contained so you can deploy any of them at will.
4. The usual terraform commands will do the work:
```bash
terraform init
terraform plan
terraform apply
```
The resource creation will take a few minutes but when its complete, you should see an output stating the command completed successfully with a list of the created resources, and some output variables with information to access your service.
__Congratulations!__ You have successfully deployed the use case you chose based on the variables configuration.
### Use case 1: Access to Cloud Run from a VM in the project
This use case deploys a Cloud Run service and a VM in the same project. To privately access Cloud Run from the VM, PSC is used. A PSC endpoint is created so that the VM can reach the service through an RFC1918 IP. Also, a DNS entry is created to point the service's default URL to that IP.
<p align="center"> <img src="images/use-case-1.png" width="600"> </p>
In this case the only variable that you need to set in `terraform.tfvars` is the main project ID:
```tfvars
prj_main_id = "[your-main-project-id]"
```
Alternatively you can pass this value on the command line:
```bash
terraform apply -var prj_main_id="[your-main-project-id]"
```
The default URL is automatically created and shown as a terraform output variable. It will be similar to the one shown in the picture above. Now SSH into the VM and run `curl`, you should see the following:
<p align="center"> <img src="images/service-running-1.png" width="700"> </p>
Note that the VM is resolving the Cloud Run service to an internal IP, 10.0.0.100. Public access is restricted, if you try to `curl` from e.g. your laptop you will get a `403 Forbidden` error.
### Use case 2: Access to Cloud Run from on-prem environment
This use case deploys a Cloud Run service in a GCP project and simulates an on-prem environment with another GCP project and a HA VPN connection between them. PSC is still used but now the DNS configuration should happen in on-premises. Since this environment is implemented in GCP, the blueprint takes care of this configuration. The PSC endpoint is announced through the VPN via BGP so that on-prem knows how to reach it. A VM is deployed in the on-prem environment to test this setup.
<p align="center"> <img src="images/use-case-2.png" width="600"> </p>
You will need to set both the main and the on-prem project IDs in `terraform.tfvars`:
```tfvars
prj_main_id = "[your-main-project-id]"
prj_onprem_id = "[your-onprem-project-id]"
```
SSH into the test VM and run `curl`, you should see the same output as in the previous use case.
### Use case 3: Access to Cloud Run from another project
Corporate apps are used by multiple teams and projects. This blueprint explores accessing from a different project to where Cloud Run is deployed. For simplicity only one more project is used but the concepts would apply to any number of projects. Three different cases are implemented:
#### Use case 3.1
The first case allows access to Cloud Run from any project as well as the Internet.
<p style="left"> <img src="images/use-case-3.1.png" width="800"> </p>
This is achieved with `ingress_settings` value set to `"all"`. This is the default if not specified but this blueprint sets it to `"internal"` instead. Add a new project and this setting in `terraform.tfvars`:
```tfvars
prj_main_id = "[your-main-project-id]"
prj_prj1_id = "[your-project1-id]"
ingress_settings = "all"
```
Note the different PSC endpoints created in each project and the different IPs. Each project can choose its own RFC1918 IP to reach the same Cloud Run service.
#### Use case 3.2
It is possible to block access from the Internet restoring `ingress_settigns` to `"internal"` but this will also block access from any other project. This feature is interesting, as will be shown in the next use case.
<p style="left"> <img src="images/use-case-3.2.png" width="800"> </p>
Simply omit `ingress_settigns` in `terraform.tfvars`:
```tfvars
prj_main_id = "[your-main-project-id]"
prj_prj1_id = "[your-project1-id]"
```
#### Use case 3.3
To allow access from other projects while keeping access from the Internet restricted, you need to add those projects to a VPC SC perimeter together with Cloud Run. Projects outisde the perimeter will be blocked. This way you can control which projects can gain access.
<p style="left"> <img src="images/use-case-3.3.png" width="800"> </p>
VPC SC requires an [Access Policy](https://cloud.google.com/access-context-manager/docs/overview#access-policies). You can use an existing policy or create a new one, but an organization can only have one organization-level access policy. The policy name is a unique numeric identifier assigned by Google Cloud.
Make sure to check out the [IAM roles](https://cloud.google.com/access-context-manager/docs/access-control) required to configure access policies and VPC SC. Also, include the identity that runs Terraform to avoid losing access from it once the perimeter is created. Set the following in `terraform.tfvars`:
```tfvars
prj_main_id = "[your-main-project-id]"
prj_prj1_id = "[your-project1-id]"
access_policy = "[policy-name]"
tf_identity = "[user or SA account]"
```
#### Use case 3.4
Another possibility is for a project to be a Service Project with the Cloud Run service running in the Host Project, since this is also considered `"internal"` traffic. In this case a VPC SC perimeter is not needed.
<p style="left"> <img src="images/use-case-3.4.png" width="800"> </p>
Note that the service project can't have a different DNS entry for the same domain, so it uses the DNS and PSC configuration of the host project. Set the following in `terraform.tfvars`:
```tfvars
prj_main_id = "[your-main-project-id]" # Used as host project
prj_svc1_id = "[your-service-project1-id]"
```
### Use case 4: Access to Cloud Run with custom domain
You need to use a L7 ILB with Serverless NEGs (in Preview) to set a custom domain for Cloud Run. As a practical example, this blueprint deploys this configuration in a Shared VPC environment with two Cloud Run services running in service projects and the ILB exposing them via a custom domain, pointing to them through a URL map: `/cart` and `/checkout`.
<p align="center"> <img src="images/use-case-4.png" width="600"> </p>
For simplicity, both services are deployed in the same service project. Also, the blueprint uses an HTTP connection to the ILB to avoid management of SSL certificates. To test access, VMs are created in the host and service projects. Set the following in `terraform.tfvars`:
```tfvars
prj_main_id = "[your-main-project-id]" # Used as host project
prj_svc1_id = "[your-service-project1-id]"
custom_domain = "cloud-run-corporate.example.org"
```
SSH into a test VM and run `curl` specifying as URL the host, your custom domain, and a path, `/cart` or `/checkout`. You will see each service responding to the request:
<p align="center"> <img src="images/service-running-4.png" width="700"> </p>
Note that the default URLs for both services are also output, and the PSC endpoint for the `*.run.app` domain from previous examples is still created. However, access to these URLs from both VMs in the host or service project is blocked since the requests come from a VPC network in a different project to the service.
## Cleaning up your environment
The easiest way to remove all the deployed resources is to run the following command:
```bash
terraform destroy
```
The above command will delete the associated resources so there will be no billable charges made afterwards. Projects are removed from Terraform state but not deleted from Google Cloud.
<!-- BEGIN TFDOC -->
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [prj_main_id](variables.tf#L78) | Main Project ID. | <code>string</code> | ✓ | |
| [access_policy](variables.tf#L17) | VPC SC access policy, if it exists. | <code>string</code> | | <code>null</code> |
| [access_policy_create](variables.tf#L23) | Parameters for the creation of a VPC SC access policy. | <code title="object&#40;&#123;&#10; parent &#61; string&#10; title &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [custom_domain](variables.tf#L32) | Custom domain for the Load Balancer. | <code>string</code> | | <code>null</code> |
| [image](variables.tf#L38) | Container image to deploy. | <code>string</code> | | <code>&#34;us-docker.pkg.dev&#47;cloudrun&#47;container&#47;hello&#34;</code> |
| [ingress_settings](variables.tf#L44) | Ingress traffic sources allowed to call the service. | <code>string</code> | | <code>&#34;internal&#34;</code> |
| [ip_ranges](variables.tf#L50) | IPs or IP ranges used by VPCs. | <code>map&#40;map&#40;string&#41;&#41;</code> | | <code title="&#123;&#10; main &#61; &#123;&#10; subnet &#61; &#34;10.0.1.0&#47;24&#34;&#10; subnet_proxy &#61; &#34;10.10.0.0&#47;24&#34;&#10; psc_addr &#61; &#34;10.0.0.100&#34;&#10; &#125;&#10; onprem &#61; &#123;&#10; subnet &#61; &#34;172.16.1.0&#47;24&#34;&#10; &#125;&#10; prj1 &#61; &#123;&#10; subnet &#61; &#34;10.0.2.0&#47;24&#34;&#10; psc_addr &#61; &#34;10.0.0.200&#34;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> |
| [prj_main_create](variables.tf#L69) | Parameters for the creation of the main project. | <code title="object&#40;&#123;&#10; billing_account_id &#61; string&#10; parent &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [prj_onprem_create](variables.tf#L83) | Parameters for the creation of an 'onprem' project. | <code title="object&#40;&#123;&#10; billing_account_id &#61; string&#10; parent &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [prj_onprem_id](variables.tf#L92) | Onprem Project ID. | <code>string</code> | | <code>null</code> |
| [prj_prj1_create](variables.tf#L98) | Parameters for the creation of project 1. | <code title="object&#40;&#123;&#10; billing_account_id &#61; string&#10; parent &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [prj_prj1_id](variables.tf#L107) | Project 1 ID. | <code>string</code> | | <code>null</code> |
| [prj_svc1_create](variables.tf#L113) | Parameters for the creation of service project 1. | <code title="object&#40;&#123;&#10; billing_account_id &#61; string&#10; parent &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [prj_svc1_id](variables.tf#L122) | Service Project 1 ID. | <code>string</code> | | <code>null</code> |
| [region](variables.tf#L128) | Cloud region where resource will be deployed. | <code>string</code> | | <code>&#34;europe-west1&#34;</code> |
| [tf_identity](variables.tf#L134) | Terraform identity to include in VPC SC perimeter. | <code>string</code> | | <code>null</code> |
## Outputs
| name | description | sensitive |
|---|---|:---:|
| [default_URL_cart](outputs.tf#L17) | Cloud Run service 'cart' default URL. | |
| [default_URL_checkout](outputs.tf#L23) | Cloud Run service 'checkout' default URL. | |
| [default_URL_hello](outputs.tf#L29) | Cloud Run service 'hello' default URL. | |
| [load_balancer_ip](outputs.tf#L34) | Load Balancer IP address. | |
<!-- END TFDOC -->
## Tests
```hcl
module "test" {
source = "./fabric/blueprints/serverless/cloud-run-corporate"
prj_main_create = {
billing_account_id = "ABCDE-12345-ABCDE"
parent = "organizations/0123456789"
}
prj_main_id = "main-project-id" # Used as host project
prj_onprem_create = {
billing_account_id = "ABCDE-12345-ABCDE"
parent = "organizations/0123456789"
}
prj_onprem_id = "onprem-project-id"
}
# tftest modules=15 resources=45
```
```hcl
module "test" {
source = "./fabric/blueprints/serverless/cloud-run-corporate"
prj_main_create = {
billing_account_id = "ABCDE-12345-ABCDE"
parent = "organizations/0123456789"
}
prj_main_id = "main-project-id"
prj_prj1_create = {
billing_account_id = "ABCDE-12345-ABCDE"
parent = "organizations/0123456789"
}
prj_prj1_id = "project1-id"
access_policy = null
access_policy_create = {
parent = "organizations/0123456789"
title = "vpcsc-cloudrun"
}
tf_identity = "user@example.org"
}
# tftest modules=15 resources=31
```
```hcl
module "test" {
source = "./fabric/blueprints/serverless/cloud-run-corporate"
prj_main_create = {
billing_account_id = "ABCDE-12345-ABCDE"
parent = "organizations/0123456789"
}
prj_main_id = "main-project-id" # Used as host project
prj_svc1_create = {
billing_account_id = "ABCDE-12345-ABCDE"
parent = "organizations/0123456789"
}
prj_svc1_id = "service-project1-id"
custom_domain = "cloud-run-corporate.example.org"
}
# tftest modules=14 resources=38
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 458 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 464 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 467 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 KiB

View File

@ -0,0 +1,608 @@
/**
* 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 {
cloud_run_domain = "run.app."
service_name_cr1 = "cart"
service_name_cr2 = "checkout"
tf_id = (var.tf_identity == null ? null :
length(regexall("iam.gserviceaccount.com", var.tf_identity)) > 0 ?
"serviceAccount:${var.tf_identity}" : "user:${var.tf_identity}")
vpc_sc_create = (length(module.project_prj1) > 0 &&
(var.access_policy != null || var.access_policy_create != null)) ? 1 : 0
}
###############################################################################
# Projects #
###############################################################################
# Main project
module "project_main" {
source = "../../../modules/project"
name = var.prj_main_id
project_create = var.prj_main_create != null
billing_account = try(var.prj_main_create.billing_account_id, null)
parent = try(var.prj_main_create.parent, null)
# Enable Shared VPC by default, some use cases will use this project as host
shared_vpc_host_config = {
enabled = true
}
services = [
"run.googleapis.com",
"compute.googleapis.com",
"dns.googleapis.com",
"cloudresourcemanager.googleapis.com",
"accesscontextmanager.googleapis.com"
]
skip_delete = true
}
# Simulated onprem environment
module "project_onprem" {
source = "../../../modules/project"
count = var.prj_onprem_id != null ? 1 : 0
name = var.prj_onprem_id
project_create = var.prj_onprem_create != null
billing_account = try(var.prj_onprem_create.billing_account_id, null)
parent = try(var.prj_onprem_create.parent, null)
services = [
"compute.googleapis.com",
"dns.googleapis.com"
]
skip_delete = true
}
# Project 1
module "project_prj1" {
source = "../../../modules/project"
count = var.prj_prj1_id != null ? 1 : 0
name = var.prj_prj1_id
project_create = var.prj_prj1_create != null
billing_account = try(var.prj_prj1_create.billing_account_id, null)
parent = try(var.prj_prj1_create.parent, null)
services = [
"compute.googleapis.com",
"dns.googleapis.com"
]
skip_delete = true
}
# Service Project 1
module "project_svc1" {
source = "../../../modules/project"
count = var.prj_svc1_id != null ? 1 : 0
name = var.prj_svc1_id
project_create = var.prj_svc1_create != null
billing_account = try(var.prj_svc1_create.billing_account_id, null)
parent = try(var.prj_svc1_create.parent, null)
shared_vpc_service_config = {
host_project = module.project_main.project_id
}
services = [
"compute.googleapis.com",
"dns.googleapis.com",
"run.googleapis.com"
]
skip_delete = true
}
###############################################################################
# Cloud Run #
###############################################################################
# Cloud Run service in main project
module "cloud_run_hello" {
source = "../../../modules/cloud-run"
project_id = module.project_main.project_id
name = "hello"
region = var.region
containers = [{
image = var.image
options = null
ports = null
resources = null
volume_mounts = null
}]
iam = {
"roles/run.invoker" = ["allUsers"]
}
ingress_settings = var.ingress_settings
}
# Cloud Run service 1 in service project
module "cloud_run_cart" {
source = "../../../modules/cloud-run"
count = var.custom_domain == null ? 0 : 1
project_id = module.project_svc1[0].project_id
name = local.service_name_cr1 # "cart"
region = var.region
containers = [{
image = var.image
options = null
ports = null
resources = null
volume_mounts = null
}]
iam = {
"roles/run.invoker" = ["allUsers"]
}
ingress_settings = var.ingress_settings
}
# Cloud Run service 2 in service project
module "cloud_run_checkout" {
source = "../../../modules/cloud-run"
count = var.custom_domain == null ? 0 : 1
project_id = module.project_svc1[0].project_id
name = local.service_name_cr2 # "checkout"
region = var.region
containers = [{
image = var.image
options = null
ports = null
resources = null
volume_mounts = null
}]
iam = {
"roles/run.invoker" = ["allUsers"]
}
ingress_settings = var.ingress_settings
}
###############################################################################
# VPCs #
###############################################################################
# VPC in main project
module "vpc_main" {
source = "../../../modules/net-vpc"
project_id = module.project_main.project_id
name = "vpc-main"
subnets = [
{
ip_cidr_range = var.ip_ranges["main"].subnet
name = "subnet-main"
region = var.region
}
]
subnets_proxy_only = [
{
ip_cidr_range = var.ip_ranges["main"].subnet_proxy
name = "subnet-proxy"
region = var.region
active = true
}
]
}
# Main VPC Firewall with default config, IAP for SSH enabled
module "firewall_main" {
source = "../../../modules/net-vpc-firewall"
project_id = module.project_main.project_id
network = module.vpc_main.name
default_rules_config = {
http_ranges = []
https_ranges = []
}
}
# VPC in simulated onprem environment
module "vpc_onprem" {
source = "../../../modules/net-vpc"
count = length(module.project_onprem)
project_id = module.project_onprem[0].project_id
name = "vpc-onprem"
subnets = [
{
ip_cidr_range = var.ip_ranges["onprem"].subnet
name = "subnet-onprem"
region = var.region
}
]
}
# Onprem VPC Firewall with default config, IAP for SSH enabled
module "firewall_onprem" {
source = "../../../modules/net-vpc-firewall"
count = length(module.project_onprem)
project_id = module.project_onprem[0].project_id
network = module.vpc_onprem[0].name
default_rules_config = {
http_ranges = []
https_ranges = []
}
}
# VPC in project 1
module "vpc_prj1" {
source = "../../../modules/net-vpc"
count = length(module.project_prj1)
project_id = module.project_prj1[0].project_id
name = "vpc-prj1"
subnets = [
{
ip_cidr_range = var.ip_ranges["prj1"].subnet
name = "subnet-prj1"
region = var.region
}
]
}
# Project 1 VPC Firewall with default config, IAP for SSH enabled
module "firewall_prj1" {
source = "../../../modules/net-vpc-firewall"
count = length(module.project_prj1)
project_id = module.project_prj1[0].project_id
network = module.vpc_prj1[0].name
default_rules_config = {
http_ranges = []
https_ranges = []
}
}
###############################################################################
# PSC #
###############################################################################
# PSC configured in the main project
module "psc_addr_main" {
source = "../../../modules/net-address"
project_id = module.project_main.project_id
psc_addresses = {
psc-addr = {
address = var.ip_ranges["main"].psc_addr
network = module.vpc_main.self_link
}
}
}
resource "google_compute_global_forwarding_rule" "psc_endpoint_main" {
provider = google-beta
project = module.project_main.project_id
name = "pscaddr"
network = module.vpc_main.self_link
ip_address = module.psc_addr_main.psc_addresses["psc-addr"].self_link
target = "vpc-sc"
load_balancing_scheme = ""
}
# PSC configured in project 1
module "psc_addr_prj1" {
source = "../../../modules/net-address"
count = length(module.project_prj1)
project_id = module.project_prj1[0].project_id
psc_addresses = {
psc-addr = {
address = var.ip_ranges["prj1"].psc_addr
network = module.vpc_prj1[0].self_link
}
}
}
resource "google_compute_global_forwarding_rule" "psc_endpoint_prj1" {
provider = google-beta
count = length(module.project_prj1)
project = module.project_prj1[0].project_id
name = "pscaddr"
network = module.vpc_prj1[0].self_link
ip_address = module.psc_addr_prj1[0].psc_addresses["psc-addr"].self_link
target = "vpc-sc"
load_balancing_scheme = ""
}
###############################################################################
# L7 ILB #
###############################################################################
module "ilb-l7" {
source = "../../../modules/net-ilb-l7"
count = var.custom_domain == null ? 0 : 1
project_id = module.project_main.project_id
name = "ilb-l7-cr"
region = var.region
backend_service_configs = {
default = {
project_id = module.project_svc1[0].project_id
backends = [{
group = "cr1"
}]
health_checks = []
}
cart = {
project_id = module.project_svc1[0].project_id
backends = [{
group = "cr1"
}]
health_checks = []
}
checkout = {
project_id = module.project_svc1[0].project_id
backends = [{
group = "cr2"
}]
health_checks = []
}
}
health_check_configs = {}
neg_configs = {
cr1 = {
project_id = module.project_svc1[0].project_id
cloudrun = {
region = var.region
target_service = {
name = local.service_name_cr1
}
}
}
cr2 = {
project_id = module.project_svc1[0].project_id
cloudrun = {
region = var.region
target_service = {
name = local.service_name_cr2
}
}
}
}
urlmap_config = {
default_service = "default"
host_rules = [{
hosts = ["*"]
path_matcher = "pathmap"
}]
path_matchers = {
pathmap = {
default_service = "default"
path_rules = [
{
paths = ["/cart", "/cart/*"]
service = local.service_name_cr1
},
{
paths = ["/checkout", "/checkout/*"]
service = local.service_name_cr2
}
]
}
}
}
vpc_config = {
network = module.vpc_main.self_link
subnetwork = module.vpc_main.subnet_self_links["${var.region}/subnet-main"]
}
}
###############################################################################
# VMs #
###############################################################################
module "vm_test_main" {
source = "../../../modules/compute-vm"
project_id = module.project_main.project_id
zone = "${var.region}-b"
name = "vm-test-main"
instance_type = "e2-micro"
network_interfaces = [{
network = module.vpc_main.self_link
subnetwork = module.vpc_main.subnet_self_links["${var.region}/subnet-main"]
}]
tags = ["ssh"]
}
module "vm_test_onprem" {
source = "../../../modules/compute-vm"
count = length(module.project_onprem)
project_id = module.project_onprem[0].project_id
zone = "${var.region}-b"
name = "vm-test-onprem"
instance_type = "e2-micro"
network_interfaces = [{
network = module.vpc_onprem[0].self_link
subnetwork = module.vpc_onprem[0].subnet_self_links["${var.region}/subnet-onprem"]
}]
tags = ["ssh"]
}
module "vm_test_prj1" {
source = "../../../modules/compute-vm"
count = length(module.project_prj1)
project_id = module.project_prj1[0].project_id
zone = "${var.region}-b"
name = "vm-test-prj1"
instance_type = "e2-micro"
network_interfaces = [{
network = module.vpc_prj1[0].self_link
subnetwork = module.vpc_prj1[0].subnet_self_links["${var.region}/subnet-prj1"]
}]
tags = ["ssh"]
}
module "vm_test_svc1" {
source = "../../../modules/compute-vm"
count = length(module.project_svc1)
project_id = module.project_svc1[0].project_id
zone = "${var.region}-b"
name = "vm-test-svc1"
instance_type = "e2-micro"
network_interfaces = [{
network = module.vpc_main.self_link
subnetwork = module.vpc_main.subnet_self_links["${var.region}/subnet-main"]
}]
tags = ["ssh"]
}
###############################################################################
# DNS #
###############################################################################
module "private_dns_main" {
source = "../../../modules/dns"
project_id = module.project_main.project_id
type = "private"
name = "dns-main"
client_networks = [module.vpc_main.self_link]
domain = local.cloud_run_domain
recordsets = {
"A *" = { records = [module.psc_addr_main.psc_addresses["psc-addr"].address] }
}
}
module "private_dns_main_custom" {
source = "../../../modules/dns"
count = var.custom_domain == null ? 0 : 1
project_id = module.project_main.project_id
type = "private"
name = "dns-main-custom"
client_networks = [module.vpc_main.self_link]
domain = format("%s.", var.custom_domain)
recordsets = {
"A " = { records = [module.ilb-l7[0].address] }
}
}
module "private_dns_onprem" {
source = "../../../modules/dns"
count = length(module.project_onprem)
project_id = module.project_onprem[0].project_id
type = "private"
name = "dns-onprem"
client_networks = [module.vpc_onprem[0].self_link]
domain = local.cloud_run_domain
recordsets = {
"A *" = { records = [module.psc_addr_main.psc_addresses["psc-addr"].address] }
}
}
module "private_dns_prj1" {
source = "../../../modules/dns"
count = length(module.project_prj1)
project_id = module.project_prj1[0].project_id
type = "private"
name = "dns-prj1"
client_networks = [module.vpc_prj1[0].self_link]
domain = local.cloud_run_domain
recordsets = {
"A *" = { records = [module.psc_addr_prj1[0].psc_addresses["psc-addr"].address] }
}
}
###############################################################################
# VPC SC #
###############################################################################
module "vpc_sc" {
source = "../../../modules/vpc-sc"
count = local.vpc_sc_create
access_policy = var.access_policy_create == null ? var.access_policy : null
access_policy_create = var.access_policy_create
ingress_policies = {
ingress-ids = {
from = {
identities = [local.tf_id]
access_levels = ["*"]
}
to = {
operations = [{ service_name = "*" }]
resources = ["*"]
}
}
}
service_perimeters_regular = {
cloudrun = {
status = {
resources = [
"projects/${module.project_main.number}",
"projects/${module.project_prj1[0].number}"
]
restricted_services = ["run.googleapis.com"]
ingress_policies = ["ingress-ids"]
}
}
}
}
###############################################################################
# VPN #
###############################################################################
# VPN between main project and "onprem" environment
module "vpn_main" {
source = "../../../modules/net-vpn-ha"
count = length(module.project_onprem)
project_id = module.project_main.project_id
region = var.region
network = module.vpc_main.self_link
name = "vpn-main-to-onprem"
peer_gateway = { gcp = module.vpn_onprem[0].self_link }
router_config = {
asn = 65001
custom_advertise = {
all_subnets = true
ip_ranges = {
(var.ip_ranges["main"].psc_addr) = "to-psc-endpoint"
}
}
}
tunnels = {
tunnel-0 = {
bgp_peer = {
address = "169.254.0.2"
asn = 65002
}
bgp_session_range = "169.254.0.1/30"
vpn_gateway_interface = 0
}
tunnel-1 = {
bgp_peer = {
address = "169.254.1.2"
asn = 65002
}
bgp_session_range = "169.254.1.1/30"
vpn_gateway_interface = 1
}
}
}
module "vpn_onprem" {
source = "../../../modules/net-vpn-ha"
count = length(module.project_onprem)
project_id = module.project_onprem[0].project_id
region = var.region
network = module.vpc_onprem[0].self_link
name = "vpn-onprem-to-main"
peer_gateway = { gcp = module.vpn_main[0].self_link }
router_config = { asn = 65002 }
tunnels = {
tunnel-0 = {
bgp_peer = {
address = "169.254.0.1"
asn = 65001
}
bgp_session_range = "169.254.0.2/30"
vpn_gateway_interface = 0
shared_secret = module.vpn_main[0].random_secret
}
tunnel-1 = {
bgp_peer = {
address = "169.254.1.1"
asn = 65001
}
bgp_session_range = "169.254.1.2/30"
vpn_gateway_interface = 1
shared_secret = module.vpn_main[0].random_secret
}
}
}

View File

@ -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.
*/
output "default_URL_cart" {
description = "Cloud Run service 'cart' default URL."
value = (var.custom_domain != null ?
module.cloud_run_cart[0].service.status[0].url : "none")
}
output "default_URL_checkout" {
description = "Cloud Run service 'checkout' default URL."
value = (var.custom_domain != null ?
module.cloud_run_checkout[0].service.status[0].url : "none")
}
output "default_URL_hello" {
description = "Cloud Run service 'hello' default URL."
value = module.cloud_run_hello.service.status[0].url
}
output "load_balancer_ip" {
description = "Load Balancer IP address."
value = var.custom_domain != null ? module.ilb-l7[0].address : "none"
}

View File

@ -0,0 +1,20 @@
/**
* 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.
*/
provider "google" {
user_project_override = true
billing_project = var.prj_main_id
}

View File

@ -0,0 +1,138 @@
/**
* 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 "access_policy" {
description = "VPC SC access policy, if it exists."
type = string
default = null
}
variable "access_policy_create" {
description = "Parameters for the creation of a VPC SC access policy."
type = object({
parent = string
title = string
})
default = null
}
variable "custom_domain" {
description = "Custom domain for the Load Balancer."
type = string
default = null
}
variable "image" {
description = "Container image to deploy."
type = string
default = "us-docker.pkg.dev/cloudrun/container/hello"
}
variable "ingress_settings" {
description = "Ingress traffic sources allowed to call the service."
type = string
default = "internal"
}
variable "ip_ranges" {
description = "IPs or IP ranges used by VPCs."
type = map(map(string))
default = {
main = {
subnet = "10.0.1.0/24"
subnet_proxy = "10.10.0.0/24"
psc_addr = "10.0.0.100"
}
onprem = {
subnet = "172.16.1.0/24"
}
prj1 = {
subnet = "10.0.2.0/24"
psc_addr = "10.0.0.200"
}
}
}
variable "prj_main_create" {
description = "Parameters for the creation of the main project."
type = object({
billing_account_id = string
parent = string
})
default = null
}
variable "prj_main_id" {
description = "Main Project ID."
type = string
}
variable "prj_onprem_create" {
description = "Parameters for the creation of an 'onprem' project."
type = object({
billing_account_id = string
parent = string
})
default = null
}
variable "prj_onprem_id" {
description = "Onprem Project ID."
type = string
default = null
}
variable "prj_prj1_create" {
description = "Parameters for the creation of project 1."
type = object({
billing_account_id = string
parent = string
})
default = null
}
variable "prj_prj1_id" {
description = "Project 1 ID."
type = string
default = null
}
variable "prj_svc1_create" {
description = "Parameters for the creation of service project 1."
type = object({
billing_account_id = string
parent = string
})
default = null
}
variable "prj_svc1_id" {
description = "Service Project 1 ID."
type = string
default = null
}
variable "region" {
description = "Cloud region where resource will be deployed."
type = string
default = "europe-west1"
}
variable "tf_identity" {
description = "Terraform identity to include in VPC SC perimeter."
type = string
default = null
}