Merge branch 'master' into jccb/apigee-fixes

This commit is contained in:
Julio Castillo 2023-09-07 16:34:45 +02:00 committed by GitHub
commit a6230d2b11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 5719 additions and 20 deletions

View File

@ -179,5 +179,5 @@ module "test" {
}
prefix = "prefix"
}
# tftest modules=9 resources=43
# tftest modules=9 resources=44
```

View File

@ -139,5 +139,5 @@ module "test" {
}
prefix = "prefix"
}
# tftest modules=5 resources=28
# tftest modules=5 resources=29
```

View File

@ -55,6 +55,7 @@ billing_account: 012345-67890A-BCDEF0
labels:
app: app-1
team: foo
parent: folders/12345678
service_encryption_key_ids:
compute:
- projects/kms-central-prj/locations/europe-west3/keyRings/my-keyring/cryptoKeys/europe3-gce
@ -71,6 +72,7 @@ service_accounts:
labels:
app: app-1
team: foo
parent: folders/12345678
service_accounts:
app-2-be: {}
@ -81,10 +83,10 @@ service_accounts:
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [factory_data](variables.tf#L83) | Project data from either YAML files or externally parsed data. | <code title="object&#40;&#123;&#10; data &#61; optional&#40;map&#40;any&#41;&#41;&#10; data_path &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | |
| [data_defaults](variables.tf#L17) | Optional default values used when corresponding project data from files are missing. | <code title="object&#40;&#123;&#10; billing_account &#61; optional&#40;string&#41;&#10; contacts &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; labels &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; metric_scopes &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; prefix &#61; optional&#40;string&#41;&#10; service_encryption_key_ids &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; service_perimeter_bridges &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; service_perimeter_standard &#61; optional&#40;string&#41;&#10; services &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; shared_vpc_service_config &#61; optional&#40;object&#40;&#123;&#10; host_project &#61; string&#10; service_identity_iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; service_iam_grants &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;, &#123; host_project &#61; null &#125;&#41;&#10; tag_bindings &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; service_accounts &#61; optional&#40;map&#40;object&#40;&#123;&#10; default_roles &#61; optional&#40;bool, true&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [data_merges](variables.tf#L44) | Optional values that will be merged with corresponding data from files. Combines with `data_defaults`, file data, and `data_overrides`. | <code title="object&#40;&#123;&#10; contacts &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; labels &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; metric_scopes &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; service_encryption_key_ids &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; service_perimeter_bridges &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; services &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; tag_bindings &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; service_accounts &#61; optional&#40;map&#40;object&#40;&#123;&#10; default_roles &#61; optional&#40;bool, true&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [data_overrides](variables.tf#L63) | Optional values that override corresponding data from files. Takes precedence over file data and `data_defaults`. | <code title="object&#40;&#123;&#10; billing_account &#61; optional&#40;string&#41;&#10; contacts &#61; optional&#40;map&#40;list&#40;string&#41;&#41;&#41;&#10; prefix &#61; optional&#40;string&#41;&#10; service_encryption_key_ids &#61; optional&#40;map&#40;list&#40;string&#41;&#41;&#41;&#10; service_perimeter_bridges &#61; optional&#40;list&#40;string&#41;&#41;&#10; service_perimeter_standard &#61; optional&#40;string&#41;&#10; tag_bindings &#61; optional&#40;map&#40;string&#41;&#41;&#10; services &#61; optional&#40;list&#40;string&#41;&#41;&#10; service_accounts &#61; optional&#40;map&#40;object&#40;&#123;&#10; default_roles &#61; optional&#40;bool, true&#41;&#10; &#125;&#41;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [factory_data](variables.tf#L85) | Project data from either YAML files or externally parsed data. | <code title="object&#40;&#123;&#10; data &#61; optional&#40;map&#40;any&#41;&#41;&#10; data_path &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | |
| [data_defaults](variables.tf#L17) | Optional default values used when corresponding project data from files are missing. | <code title="object&#40;&#123;&#10; billing_account &#61; optional&#40;string&#41;&#10; contacts &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; labels &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; metric_scopes &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; parent &#61; optional&#40;string&#41;&#10; prefix &#61; optional&#40;string&#41;&#10; service_encryption_key_ids &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; service_perimeter_bridges &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; service_perimeter_standard &#61; optional&#40;string&#41;&#10; services &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; shared_vpc_service_config &#61; optional&#40;object&#40;&#123;&#10; host_project &#61; string&#10; service_identity_iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; service_iam_grants &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;, &#123; host_project &#61; null &#125;&#41;&#10; tag_bindings &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; service_accounts &#61; optional&#40;map&#40;object&#40;&#123;&#10; default_roles &#61; optional&#40;bool, true&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [data_merges](variables.tf#L45) | Optional values that will be merged with corresponding data from files. Combines with `data_defaults`, file data, and `data_overrides`. | <code title="object&#40;&#123;&#10; contacts &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; labels &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; metric_scopes &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; service_encryption_key_ids &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; service_perimeter_bridges &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; services &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; tag_bindings &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; service_accounts &#61; optional&#40;map&#40;object&#40;&#123;&#10; default_roles &#61; optional&#40;bool, true&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [data_overrides](variables.tf#L64) | Optional values that override corresponding data from files. Takes precedence over file data and `data_defaults`. | <code title="object&#40;&#123;&#10; billing_account &#61; optional&#40;string&#41;&#10; contacts &#61; optional&#40;map&#40;list&#40;string&#41;&#41;&#41;&#10; parent &#61; optional&#40;string&#41;&#10; prefix &#61; optional&#40;string&#41;&#10; service_encryption_key_ids &#61; optional&#40;map&#40;list&#40;string&#41;&#41;&#41;&#10; service_perimeter_bridges &#61; optional&#40;list&#40;string&#41;&#41;&#10; service_perimeter_standard &#61; optional&#40;string&#41;&#10; tag_bindings &#61; optional&#40;map&#40;string&#41;&#41;&#10; services &#61; optional&#40;list&#40;string&#41;&#41;&#10; service_accounts &#61; optional&#40;map&#40;object&#40;&#123;&#10; default_roles &#61; optional&#40;bool, true&#41;&#10; &#125;&#41;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
## Outputs

View File

@ -28,11 +28,11 @@ locals {
)
projects = {
for k, v in local._data : k => merge(v, {
billing_account = coalesce(
billing_account = try(coalesce(
var.data_overrides.billing_account,
try(v.billing_account, null),
var.data_defaults.billing_account
)
), null)
contacts = coalesce(
var.data_overrides.contacts,
try(v.contacts, null),
@ -46,6 +46,11 @@ locals {
try(v.metric_scopes, null),
var.data_defaults.metric_scopes
)
parent = coalesce(
var.data_overrides.parent,
try(v.parent, null),
var.data_defaults.parent
)
prefix = coalesce(
var.data_overrides.prefix,
try(v.prefix, null),

View File

@ -21,6 +21,7 @@ variable "data_defaults" {
contacts = optional(map(list(string)), {})
labels = optional(map(string), {})
metric_scopes = optional(list(string), [])
parent = optional(string)
prefix = optional(string)
service_encryption_key_ids = optional(map(list(string)), {})
service_perimeter_bridges = optional(list(string), [])
@ -65,6 +66,7 @@ variable "data_overrides" {
type = object({
billing_account = optional(string)
contacts = optional(map(list(string)))
parent = optional(string)
prefix = optional(string)
service_encryption_key_ids = optional(map(list(string)))
service_perimeter_bridges = optional(list(string))

View File

@ -214,5 +214,5 @@ module "test" {
}
}
# tftest modules=4 resources=18
# tftest modules=4 resources=19
```

View File

@ -6,12 +6,18 @@ The blueprints in this folder show how to automate installation of specific thir
### OpenShift cluster bootstrap on Shared VPC
<a href="./openshift/" title="HubOpenShift bootstrap example"><img src="./openshift/diagram.png" align="left" width="280px"></a> This [example](./openshift/) shows how to quickly bootstrap an OpenShift 4.7 cluster on GCP, using typical enterprise features like Shared VPC and CMEK for instance disks.
<a href="./openshift/" title="HubOpenShift bootstrap example"><img src="./openshift/diagram.png" align="left" width="320px"></a> <p style="margin-left: 340px"> This [example](./openshift/) shows how to quickly bootstrap an OpenShift 4.7 cluster on GCP, using typical enterprise features like Shared VPC and CMEK for instance disks. </p>
<br clear="left">
### Wordpress deployment on Cloud Run
<a href="./wordpress/cloudrun/" title="Wordpress deployment on Cloud Run"><img src="./wordpress/cloudrun/architecture.png" align="left" width="280px"></a> This [example](./wordpress/cloudrun/) shows how to deploy a functioning new Wordpress website exposed to the public internet via CloudRun and Cloud SQL, with minimal technical overhead.
<a href="./wordpress/cloudrun/" title="Wordpress deployment on Cloud Run"><img src="./wordpress/cloudrun/images/architecture.png" align="left" width="320px"></a> <p style="margin-left: 340px"> This [example](./wordpress/cloudrun/) shows how to deploy a functioning new Wordpress website exposed to the public internet via CloudRun and Cloud SQL, with minimal technical overhead. </p>
<br clear="left">
### Serverless phpIPAM on Cloud Run
<a href="./phpipam/" title="phpIPAM bootstrap example"><img src="./phpipam/images/phpipam.png" align="left" width="320px"></a> <p style="margin-left: 340px">This [example](./phpipam/) shows how to quickly bootstrap a serverless phpIPAM instance on GCP using Cloud Run. This comes with typical enterprise features like Shared VPC, Cloud Armor with IAP and, possibly, private exposure via Internal Application Load Balancer. Indeed, the script supports deploying the application either publicly via Global Application Load Balancer with restricted access based on IPs (Cloud Armor) and identities (Identity Aware Proxy) or privately via Internal Application Load Balancer.</p>
<br clear="left">

View File

@ -0,0 +1,239 @@
# Serverless phpIPAM on Cloud Run
[phpIPAM](https://phpipam.net/) is an open-source IP address management (IPAM)
system that can be used to manage IP addresses in both on-premises and cloud
environments. It is a powerful tool that can help businesses to automate IP
address management, proactively identify and resolve IP address conflicts, and
plan for future IP address needs.
This repository aims to speed up deployment of phpIPAM software on Google Cloud
Platform Cloud Run serverless product. The web application can be exposed either
publicly via Global Application Load Balancer or internally via Internal
Application Load Balancer. More information on the architecture section.
## Architecture
![Serverless phpIPAM on Cloud Run](images/phpipam.png "Wordpress on Cloud Run")
The main components that are deployed in this architecture are the following (
you can learn about them by following the hyperlinks):
- [Cloud Run](https://cloud.google.com/run): serverless PaaS offering to host
containers for web-oriented applications, while offering security, scalability
and easy versioning
- [Cloud SQL](https://cloud.google.com/sql): Managed solution for SQL databases
- [VPC Serverless Connector](https://cloud.google.com/vpc/docs/serverless-vpc-access):
Solution to access the CloudSQL VPC from Cloud Run, using only internal IP
addresses
- [Global Application Load Balancer](https://cloud.google.com/load-balancing/docs/https) (\*):
An external Application Load Balancer is a proxy-based Layer 7 load balancer
that enables you to run and scale your services behind a single external IP
address.
- [Cloud Armor](https://cloud.google.com/armor/docs/cloud-armor-overview) (\*):
Help protect your applications and websites against denial of service and web
attacks.
- [Identity Aware Proxy](https://cloud.google.com/iap/docs/concepts-overview) (\*):
IAP lets you establish a central authorization layer for applications accessed
by HTTPS, so you can use an application-level access control model instead of
relying on network-level firewalls.
- [Regional Internal Application Load Balancer](https://cloud.google.com/load-balancing/docs/l7-internal) (\*):
A Google Cloud internal Application Load Balancer is a regional proxy-based
layer 7 load balancer that enables you expose your services behind a single
internal IP address.
> (\*) Product deployment depends on input variables
## Setup
### Prerequisites
#### Setting up the project for the deployment
This example will deploy all its resources into the project defined by
the `project_id` variable. Please note that we assume this project already
exists. However, if you provide the appropriate values to the `project_create`
variable, the project will be created as part of the deployment.
If `project_create` is left to null, the identity performing the deployment
needs the `owner` role on the project defined by the `project_id` variable.
Otherwise, the identity performing the deployment
needs `resourcemanager.projectCreator` on the resource hierarchy node specified
by `project_create.parent` and `billing.user` on the billing account specified
by `project_create.billing_account_id`.
### Deployment
#### Step 0: Cloning the repository
If you want to deploy from your Cloud Shell, click on the image below, sign in
if required and when the prompt appears, click on “confirm”.
[![Open Cloudshell](../../../assets/images/cloud-shell-button.png)](https://shell.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https%3A%2F%2Fgithub.com%2FGoogleCloudPlatform%2Fcloud-foundation-fabric&cloudshell_workspace=blueprints%2Fthird-party-solutions%2Fwordpress%2Fcloudrun)
Otherwise, in your console of choice:
```bash
git clone https://github.com/GoogleCloudPlatform/cloud-foundation-fabric
```
Before you deploy the architecture, you will need at least the following
information (for more precise configuration see the Variables section):
* The project ID.
#### Step 2: Prepare the variables
Once you have the required information, head back to your cloned repository.
Make sure youre in the directory of this tutorial (where this README is in).
Configure the Terraform variables in your `terraform.tfvars` file.
See [terraform.tfvars.sample](terraform.tfvars.sample) as starting point - just
copy it to `terraform.tfvars` and edit the latter. See the variables
documentation below.
**Notes**:
1. If you have
the [domain restriction org. policy](https://cloud.google.com/resource-manager/docs/organization-policy/restricting-domains)
on your organization, you have to edit the `cloud_run_invoker` variable and
give it a value that will be accepted in accordance to your policy.
2. By default, the application will be exposed externally through Global
Application Load Balancer, for restricting access to specific identities
please check IAP configuration or deploy the application internally via the
ILB
3. Setting the `phpipam_exposure` variable to "INTERNAL" will deploy an Internal
Application Load Balancer on the same VPC. This might be the preferred option
for enterprises since it prevents exposing the application publicly still
allowing internal access through private network (via either VPN and/or
Interconnect)
#### Step 3: Deploy resources
Initialize your Terraform environment and deploy the resources:
```shell
terraform init
terraform apply
```
#### Step 4: Use the created resources
Upon completion, you will see the output with the values for the Cloud Run
service and the user and password to access the application.
You can also view it later with:
```shell
terraform output
# or for the concrete variable:
terraform output cloud_run_service
```
Please be aware that the password created in the script is not yet configured in the
application, you will be prompted to insert that during phpIPAM installation
process at first login.
To access the newly deployed application follow these instructions:
1. Get the default phpIPAM url from the terraform output in the form
{IP_ADDRESS}.nip.io
2. Open your browser at that URL and you will see your phpIPAM installation page
like the following one:
![phpIPAM Installation page](images/phpipam_install.png "phpIPAM installation page")
3. Click on "New phpipam installation". On the next page click "Automatic
database installation", you will be prompted to the following form:
![phpIPAM DB install](images/phpipam_db.png "phpIPAM DB installation")
4. Insert "admin" as the MySQL username and the password available on the
terraform output of this command below (without quotes).
Untick the "Create new database" otherwise you'll get an error during
installation, leave all the other values as default and then click on "
Install phpipam database"
```
terraform output cloudsql_password
```
5. After some time a "Database installed successfully!" message should pop up.
Then click "continue" and you'll be prompted to the last form for configuring
admin credentials:
![phpIPAM Admin setup](images/phpipam_admin.png "phpIPAM DB installation")
6. Insert the phpipam password available in the output of the following command
and choose a site title. Then insert the site url and click "Save
settings". "A Settings updated, installation complete!" message should pop up
and clicking "Proceed to login." will redirect you to the login page.
Be aware this is just a convenient way to have a backup admin password in
terraform, you could use whatever password you prefer.
```
terraform output phpipam_password
```
7. Insert "admin" as username and the password configured on the previous step
and after login you'll finally get to the phpIPAM homepage.
![phpIPAM Homepage](images/phpipam_home.png "phpIPAM Homepage")
### Cleaning up your environment
The easiest way to remove all the deployed resources is to run the following
command in Cloud Shell:
``` {shell}
terraform destroy
```
The above command will delete the associated resources so there will be no
billable charges made afterwards.
<!-- BEGIN TFDOC -->
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [prefix](variables.tf#L109) | Prefix used for resource names. | <code>string</code> | ✓ | |
| [project_id](variables.tf#L128) | Project id, references existing project if `project_create` is null. | <code>string</code> | ✓ | |
| [admin_principals](variables.tf#L19) | Users, groups and/or service accounts that are assigned roles, in IAM format (`group:foo@example.com`). | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [cloud_run_invoker](variables.tf#L25) | IAM member authorized to access the end-point (for example, 'user:YOUR_IAM_USER' for only you or 'allUsers' for everyone). | <code>string</code> | | <code>&#34;allUsers&#34;</code> |
| [cloudsql_password](variables.tf#L31) | CloudSQL password (will be randomly generated by default). | <code>string</code> | | <code>null</code> |
| [connector](variables.tf#L37) | Existing VPC serverless connector to use if not creating a new one. | <code>string</code> | | <code>null</code> |
| [create_connector](variables.tf#L43) | Should a VPC serverless connector be created or not. | <code>bool</code> | | <code>true</code> |
| [custom_domain](variables.tf#L49) | Cloud Run service custom domain for GLB. | <code>string</code> | | <code>null</code> |
| [iap](variables.tf#L55) | Identity-Aware Proxy for Cloud Run in the LB. | <code title="object&#40;&#123;&#10; enabled &#61; optional&#40;bool, false&#41;&#10; app_title &#61; optional&#40;string, &#34;Cloud Run Explore Application&#34;&#41;&#10; oauth2_client_name &#61; optional&#40;string, &#34;Test Client&#34;&#41;&#10; email &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [ip_ranges](variables.tf#L67) | CIDR blocks: VPC serverless connector, Private Service Access(PSA) for CloudSQL, CloudSQL VPC. | <code title="object&#40;&#123;&#10; connector &#61; string&#10; psa &#61; string&#10; ilb &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; connector &#61; &#34;10.8.0.0&#47;28&#34;&#10; psa &#61; &#34;10.60.0.0&#47;24&#34;&#10; ilb &#61; &#34;10.128.0.0&#47;28&#34;&#10;&#125;">&#123;&#8230;&#125;</code> |
| [phpipam_config](variables.tf#L81) | PHPIpam configuration. | <code title="object&#40;&#123;&#10; image &#61; optional&#40;string, &#34;phpipam&#47;phpipam-www:latest&#34;&#41;&#10; port &#61; optional&#40;number, 80&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; image &#61; &#34;phpipam&#47;phpipam-www:latest&#34;&#10; port &#61; 80&#10;&#125;">&#123;&#8230;&#125;</code> |
| [phpipam_exposure](variables.tf#L93) | Whether to expose the application publicly via GLB or internally via ILB, default GLB. | <code>string</code> | | <code>&#34;EXTERNAL&#34;</code> |
| [phpipam_password](variables.tf#L103) | Password for the phpipam user (will be randomly generated by default). | <code>string</code> | | <code>null</code> |
| [project_create](variables.tf#L119) | Provide values if project creation is needed, uses existing project if null. Parent is in 'folders/nnn' or 'organizations/nnn' format. | <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> |
| [region](variables.tf#L133) | Region for the created resources. | <code>string</code> | | <code>&#34;europe-west4&#34;</code> |
| [security_policy](variables.tf#L139) | Security policy (Cloud Armor) to enforce in the LB. | <code title="object&#40;&#123;&#10; enabled &#61; optional&#40;bool, false&#41;&#10; ip_blacklist &#61; optional&#40;list&#40;string&#41;, &#91;&#34;&#42;&#34;&#93;&#41;&#10; path_blocked &#61; optional&#40;string, &#34;&#47;login.html&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [vpc_config](variables.tf#L149) | VPC Network and subnetwork self links for internal LB setup. | <code title="object&#40;&#123;&#10; network &#61; string&#10; subnetwork &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
## Outputs
| name | description | sensitive |
|---|---|:---:|
| [cloud_run_service](outputs.tf#L17) | CloudRun service URL. | ✓ |
| [cloudsql_password](outputs.tf#L23) | CloudSQL password. | ✓ |
| [phpipam_ip_address](outputs.tf#L29) | PHPIPAM IP Address either external or internal according to app exposure. | |
| [phpipam_password](outputs.tf#L34) | PHPIPAM user password. | ✓ |
| [phpipam_url](outputs.tf#L40) | PHPIPAM website url. | |
| [phpipam_user](outputs.tf#L45) | PHPIPAM username. | |
<!-- END TFDOC -->
## Test
```hcl
module "test" {
source = "./fabric/blueprints/third-party-solutions/phpipam"
admin_principals = ["group:foo@example.com"]
prefix = "test"
project_create = {
billing_account_id = "1234-ABCD-1234"
parent = "folders/1234563"
}
project_id = "test-prj"
}
# tftest modules=7 resources=43
```

View File

@ -0,0 +1,31 @@
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
# Set up CloudSQL
module "cloudsql" {
source = "../../../modules/cloudsql-instance"
project_id = module.project.project_id
name = "${var.prefix}-mysql"
database_version = local.cloudsql_conf.database_version
databases = [local.cloudsql_conf.db]
network = local.network
prefix = var.prefix
region = var.region
tier = local.cloudsql_conf.tier
users = {
"${local.cloudsql_conf.user}" = var.cloudsql_password
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,153 @@
/**
* 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 {
glb_create = var.phpipam_exposure == "EXTERNAL"
iap_sa_email = try(module.project.service_accounts.robots["iap"].email, "")
}
# Reserved static IP for the Load Balancer
module "addresses" {
source = "../../../modules/net-address"
count = local.glb_create ? 1 : 0
project_id = var.project_id
global_addresses = ["phpipam"]
}
# Global L7 HTTPS Load Balancer in front of Cloud Run
module "glb" {
source = "../../../modules/net-lb-app-ext"
count = local.glb_create ? 1 : 0
project_id = module.project.project_id
name = "phpipam-glb"
address = module.addresses.0.global_addresses["phpipam"].address
protocol = "HTTPS"
backend_service_configs = {
default = {
backends = [
{ backend = "phpipam" }
]
health_checks = []
port_name = "http"
security_policy = try(google_compute_security_policy.policy[0].name,
null)
iap_config = try({
oauth2_client_id = google_iap_client.iap_client[0].client_id,
oauth2_client_secret = google_iap_client.iap_client[0].secret
}, null)
}
}
health_check_configs = {}
neg_configs = {
phpipam = {
cloudrun = {
region = var.region
target_service = {
name = module.cloud_run.service_name
}
}
}
}
ssl_certificates = {
managed_configs = {
default = {
domains = [local.domain]
}
}
}
}
# Cloud Armor configuration
resource "google_compute_security_policy" "policy" {
count = local.glb_create && var.security_policy.enabled ? 1 : 0
project = module.project.project_id
name = "cloud-run-policy"
rule {
action = "deny(403)"
priority = 1000
match {
versioned_expr = "SRC_IPS_V1"
config {
src_ip_ranges = var.security_policy.ip_blacklist
}
}
description = "Deny access to list of IPs"
}
rule {
action = "deny(403)"
priority = 900
match {
expr {
expression = "request.path.matches(\"${var.security_policy.path_blocked}\")"
}
}
description = "Deny access to specific URL paths"
}
rule {
action = "allow"
priority = "2147483647"
match {
versioned_expr = "SRC_IPS_V1"
config {
src_ip_ranges = ["*"]
}
}
description = "Default rule"
}
}
# Identity-Aware Proxy (IAP) or OAuth brand (see OAuth consent screen)
# Note:
# Only "Organization Internal" brands can be created programmatically
# via API. To convert it into an external brand please use the GCP
# Console.
# Brands can only be created once for a Google Cloud project and the
# underlying Google API doesn't support DELETE or PATCH methods.
# Destroying a Terraform-managed Brand will remove it from state but
# will not delete it from Google Cloud.
resource "google_iap_brand" "iap_brand" {
count = local.glb_create && var.iap.enabled ? 1 : 0
project = module.project.project_id
# Support email displayed on the OAuth consent screen. The caller must be
# the user with the associated email address, or if a group email is
# specified, the caller can be either a user or a service account which
# is an owner of the specified group in Cloud Identity.
support_email = var.iap.email
application_title = var.iap.app_title
}
# IAP owned OAuth2 client
# Note:
# Only internal org clients can be created via declarative tools.
# External clients must be manually created via the GCP console.
# Warning:
# All arguments including secret will be stored in the raw state as plain-text.
resource "google_iap_client" "iap_client" {
count = local.glb_create && var.iap.enabled ? 1 : 0
display_name = var.iap.oauth2_client_name
brand = google_iap_brand.iap_brand[0].name
}
# IAM policy for IAP
# For simplicity we use the same email as support_email and authorized member
resource "google_iap_web_iam_member" "iap_iam" {
count = local.glb_create && var.iap.enabled ? 1 : 0
project = module.project.project_id
role = "roles/iap.httpsResourceAccessor"
member = "user:${var.iap.email}"
}

View File

@ -0,0 +1,89 @@
/**
* 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 {
ilb_create = var.phpipam_exposure == "INTERNAL"
}
# default ssl certificate
resource "tls_private_key" "default" {
algorithm = "RSA"
rsa_bits = 2048
}
resource "tls_self_signed_cert" "default" {
private_key_pem = tls_private_key.default.private_key_pem
validity_period_hours = 720
allowed_uses = [
"key_encipherment",
"digital_signature",
"server_auth",
]
subject {
common_name = local.domain
organization = "ACME Examples, Inc"
}
}
module "ilb-l7" {
source = "../../../modules/net-lb-app-int"
count = local.ilb_create ? 1 : 0
project_id = var.project_id
name = "ilb-l7-cr"
protocol = "HTTPS"
region = var.region
backend_service_configs = {
default = {
project_id = var.project_id
backends = [
{
group = "phpipam"
}
]
health_checks = []
}
}
health_check_configs = {
default = {
https = { port = 443 }
}
}
neg_configs = {
phpipam = {
project_id = var.project_id
cloudrun = {
region = var.region
target_service = {
name = module.cloud_run.service_name
}
}
}
}
ssl_certificates = {
create_configs = {
default = {
# certificate and key could also be read via file() from external files
certificate = tls_self_signed_cert.default.cert_pem
private_key = tls_private_key.default.private_key_pem
}
}
}
vpc_config = {
network = local.network
subnetwork = local.subnetwork
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

View File

@ -0,0 +1,144 @@
/**
* 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 {
cloudsql_conf = {
database_version = "MYSQL_8_0"
tier = "db-g1-small"
db = "phpipam"
user = "admin"
}
connector = var.connector == null ? module.cloud_run.vpc_connector : var.connector
domain = (
var.custom_domain != null ? var.custom_domain : (
var.phpipam_exposure == "EXTERNAL" ?
"${module.addresses.0.global_addresses["phpipam"].address}.nip.io" : "phpipam.internal")
)
iam = {
# CloudSQL
"roles/cloudsql.admin" = var.admin_principals
"roles/cloudsql.client" = var.admin_principals
"roles/cloudsql.instanceUser" = var.admin_principals
# common roles
"roles/logging.admin" = var.admin_principals
"roles/iam.serviceAccountUser" = var.admin_principals
"roles/iam.serviceAccountTokenCreator" = var.admin_principals
}
network = var.vpc_config == null ? module.vpc.0.self_link : var.vpc_config.network
phpipam_password = var.phpipam_password == null ? random_password.phpipam_password.result : var.phpipam_password
subnetwork = var.vpc_config == null ? module.vpc.0.subnet_self_links["${var.region}/ilb"] : var.vpc_config.subnetwork
}
# either create a project or set up the given one
module "project" {
source = "../../../modules/project"
billing_account = try(var.project_create.billing_account_id, null)
iam = var.project_create != null ? local.iam : {}
name = var.project_id
parent = try(var.project_create.parent, null)
prefix = var.project_create == null ? null : var.prefix
project_create = var.project_create != null
services = [
"iap.googleapis.com",
"logging.googleapis.com",
"monitoring.googleapis.com",
"run.googleapis.com",
"servicenetworking.googleapis.com",
"sqladmin.googleapis.com",
"sql-component.googleapis.com",
"vpcaccess.googleapis.com"
]
}
# create a VPC for CloudSQL and ILB
module "vpc" {
source = "../../../modules/net-vpc"
count = var.vpc_config == null ? 1 : 0
project_id = module.project.project_id
name = "${var.prefix}-sql-vpc"
psa_config = {
ranges = {
cloud-sql = var.ip_ranges.psa
}
}
subnets = [
{
ip_cidr_range = var.ip_ranges.ilb
name = "ilb"
region = var.region
}
]
}
resource "random_password" "phpipam_password" {
length = 8
}
# create the Cloud Run service
module "cloud_run" {
source = "../../../modules/cloud-run"
project_id = module.project.project_id
name = "${var.prefix}-cr-phpipam"
prefix = var.prefix
ingress_settings = "all"
region = var.region
containers = {
phpipam = {
image = var.phpipam_config.image
ports = {
http = {
name = "http1"
protocol = null
container_port = var.phpipam_config.port
}
}
env_from = null
# set up the database connection
env = {
"TZ" = "Europe/Rome"
"IPAM_DATABASE_HOST" = module.cloudsql.ip
"IPAM_DATABASE_USER" = local.cloudsql_conf.user
"IPAM_DATABASE_PASS" = var.cloudsql_password == null ? module.cloudsql.user_passwords[local.cloudsql_conf.user] : var.cloudsql_password
"IPAM_DATABASE_NAME" = local.cloudsql_conf.db
"IPAM_DATABASE_PORT" = "3306"
}
}
}
iam = local.glb_create && var.iap.enabled ? {
"roles/run.invoker" : ["serviceAccount:${local.iap_sa_email}"]
} : {
"roles/run.invoker" : [var.cloud_run_invoker]
}
revision_annotations = {
autoscaling = {
min_scale = 1
max_scale = 2
}
# connect to CloudSQL
cloudsql_instances = [module.cloudsql.connection_name]
# allow all traffic
vpcaccess_egress = "private-ranges-only"
vpcaccess_connector = local.connector
}
vpc_connector_create = var.create_connector ? {
ip_cidr_range = var.ip_ranges.connector
vpc_self_link = local.network
} : null
}

View File

@ -0,0 +1,48 @@
/**
* 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 "cloud_run_service" {
description = "CloudRun service URL."
value = module.cloud_run.service.status[0].url
sensitive = true
}
output "cloudsql_password" {
description = "CloudSQL password."
value = var.cloudsql_password == null ? module.cloudsql.user_passwords[local.cloudsql_conf.user] : var.cloudsql_password
sensitive = true
}
output "phpipam_ip_address" {
description = "PHPIPAM IP Address either external or internal according to app exposure."
value = local.glb_create ? module.addresses.0.global_addresses["phpipam"].address : module.ilb-l7.0.address
}
output "phpipam_password" {
description = "PHPIPAM user password."
value = local.phpipam_password
sensitive = true
}
output "phpipam_url" {
description = "PHPIPAM website url."
value = local.domain
}
output "phpipam_user" {
description = "PHPIPAM username."
value = "admin"
}

View File

@ -0,0 +1,2 @@
prefix = "phpipam"
project_id = "my-phpipam-project"

View File

@ -0,0 +1,156 @@
/**
* 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.
*/
# Documentation: https://cloud.google.com/run/docs/securing/managing-access#making_a_service_public
variable "admin_principals" {
description = "Users, groups and/or service accounts that are assigned roles, in IAM format (`group:foo@example.com`)."
type = list(string)
default = []
}
variable "cloud_run_invoker" {
description = "IAM member authorized to access the end-point (for example, 'user:YOUR_IAM_USER' for only you or 'allUsers' for everyone)."
type = string
default = "allUsers"
}
variable "cloudsql_password" {
description = "CloudSQL password (will be randomly generated by default)."
type = string
default = null
}
variable "connector" {
description = "Existing VPC serverless connector to use if not creating a new one."
type = string
default = null
}
variable "create_connector" {
description = "Should a VPC serverless connector be created or not."
type = bool
default = true
}
variable "custom_domain" {
description = "Cloud Run service custom domain for GLB."
type = string
default = null
}
variable "iap" {
description = "Identity-Aware Proxy for Cloud Run in the LB."
type = object({
enabled = optional(bool, false)
app_title = optional(string, "Cloud Run Explore Application")
oauth2_client_name = optional(string, "Test Client")
email = optional(string)
})
default = {}
}
# PSA: documentation: https://cloud.google.com/vpc/docs/configure-private-services-access#allocating-range
variable "ip_ranges" {
description = "CIDR blocks: VPC serverless connector, Private Service Access(PSA) for CloudSQL, CloudSQL VPC."
type = object({
connector = string
psa = string
ilb = string
})
default = {
connector = "10.8.0.0/28"
psa = "10.60.0.0/24"
ilb = "10.128.0.0/28"
}
}
variable "phpipam_config" {
description = "PHPIpam configuration."
type = object({
image = optional(string, "phpipam/phpipam-www:latest")
port = optional(number, 80)
})
default = {
image = "phpipam/phpipam-www:latest"
port = 80
}
}
variable "phpipam_exposure" {
description = "Whether to expose the application publicly via GLB or internally via ILB, default GLB."
type = string
default = "EXTERNAL"
validation {
condition = var.phpipam_exposure == "INTERNAL" || var.phpipam_exposure == "EXTERNAL"
error_message = "phpipam_exposure supports only 'INTERNAL' or 'EXTERNAL'"
}
}
variable "phpipam_password" {
description = "Password for the phpipam user (will be randomly generated by default)."
type = string
default = null
}
variable "prefix" {
description = "Prefix used for resource names."
type = string
nullable = false
validation {
condition = var.prefix != ""
error_message = "Prefix cannot be empty."
}
}
variable "project_create" {
description = "Provide values if project creation is needed, uses existing project if null. Parent is in 'folders/nnn' or 'organizations/nnn' format."
type = object({
billing_account_id = string
parent = string
})
default = null
}
variable "project_id" {
description = "Project id, references existing project if `project_create` is null."
type = string
}
variable "region" {
description = "Region for the created resources."
type = string
default = "europe-west4"
}
variable "security_policy" {
description = "Security policy (Cloud Armor) to enforce in the LB."
type = object({
enabled = optional(bool, false)
ip_blacklist = optional(list(string), ["*"])
path_blocked = optional(string, "/login.html")
})
default = {}
}
variable "vpc_config" {
description = "VPC Network and subnetwork self links for internal LB setup."
type = object({
network = string
subnetwork = string
})
default = null
}

View File

@ -23,7 +23,7 @@ essential_contacts:
- team-a-contacts@example.com
# Folder the project will be created as children of
folder_id: folders/012345678901
parent: folders/012345678901
# [opt] Authoritative IAM bindings in group => [roles] format
group_iam:

View File

@ -221,6 +221,7 @@
service_agent: "service-%s@gcp-sa-healthcare.iam.gserviceaccount.com"
- name: "iap"
service_agent: "service-%s@gcp-sa-iap.iam.gserviceaccount.com"
jit: true
- name: "identitytoolkit"
service_agent: "service-%s@gcp-sa-identitytoolkit.iam.gserviceaccount.com"
- name: "ids"

View File

@ -30,7 +30,7 @@ values:
module.project-factory.module.projects["prj-app-1"].google_project.project[0]:
auto_create_network: false
billing_account: 012345-67890A-BCDEF0
folder_id: null
folder_id: "12345678"
labels:
app: app-1
team: foo
@ -61,7 +61,7 @@ values:
module.project-factory.module.projects["prj-app-2"].google_project.project[0]:
auto_create_network: false
billing_account: 012345-67890A-ABCDEF
folder_id: null
folder_id: "12345678"
labels:
app: app-1
team: foo

View File

@ -14,4 +14,4 @@
counts:
modules: 28
resources: 151
resources: 154

View File

@ -14,4 +14,4 @@
counts:
modules: 30
resources: 188
resources: 191

View File

@ -14,4 +14,4 @@
counts:
modules: 42
resources: 197
resources: 200

View File

@ -14,4 +14,4 @@
counts:
modules: 21
resources: 168
resources: 170

View File

@ -14,4 +14,4 @@
counts:
modules: 36
resources: 208
resources: 211

View File

@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
parent_id: folders/012345678901
parent: folders/012345678901
services:
- storage.googleapis.com
- stackdriver.googleapis.com