Skip to content

Commit

Permalink
feat: add hello-world as the default application (#247)
Browse files Browse the repository at this point in the history
Co-authored-by: Andrew Peabody <andrewpeabody@google.com>
  • Loading branch information
caetano-colin and apeabody authored Oct 10, 2024
1 parent e4a547b commit da26d9b
Show file tree
Hide file tree
Showing 43 changed files with 718 additions and 40 deletions.
13 changes: 5 additions & 8 deletions 2-multitenant/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ The steps below assume that you are checkout out on the same level as `terraform
└── .
```

> NOTE: If you don't have the foundation codebase, you can clone it by running the following command: `git clone --branch v4.1.0 https://github.com/terraform-google-modules/terraform-example-foundation.git`
1. Retrieve Multi-tenant administration project variable value from 1-bootstrap:

```bash
Expand Down Expand Up @@ -77,17 +79,12 @@ The steps below assume that you are checkout out on the same level as `terraform
[README.md](./envs/production/README.md#inputs) files for additional information
on the values in the `terraform.tfvars` file. In addition to `envs` from
prerequisites, each App must have it's own entry under `apps` with a list of any
dedicated IP address to be provisioned.
dedicated IP address to be provisioned. For the default hello world example, use the following values
```terraform
apps = {
"my-app" : {
"ip_address_names" : [
"my-app-ip",
]
"certificates" : {
"my-app-cert" : ["my-domain-name"]
}
"default-example" : {
"acronym" = "de",
}
}
```
Expand Down
2 changes: 1 addition & 1 deletion 2-multitenant/envs/development/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| apps | A map, where the key is the application name, defining the application configurations with the following properties:<br>- **acronym** (Required): A short identifier for the application with a maximum of 3 characters in length.<br>- **ip\_address\_names** (Optional): A list of IP address names associated with the application.<br>- **certificates** (Optional): A map of certificate names to a list of certificate values required by the application. | <pre>map(object({<br> acronym = string<br> ip_address_names = optional(list(string))<br> certificates = optional(map(list(string)))<br> }))</pre> | n/a | yes |
| apps | A map, where the key is the application name, defining the application configurations with the following properties:<br>- **acronym** (Required): A short identifier for the application with a maximum of 3 characters in length.<br>- **ip\_address\_names** (Optional): A list of IP address names associated with the application.<br>- **certificates** (Optional): A map of certificate names to a list of certificate values required by the application. | <pre>map(object({<br> acronym = string<br> ip_address_names = optional(list(string), [])<br> certificates = optional(map(list(string)), {})<br> }))</pre> | n/a | yes |
| envs | Environments | <pre>map(object({<br> billing_account = string<br> folder_id = string<br> network_project_id = string<br> network_self_link = string<br> org_id = string<br> subnets_self_links = list(string)<br> }))</pre> | n/a | yes |

## Outputs
Expand Down
4 changes: 2 additions & 2 deletions 2-multitenant/envs/development/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ variable "apps" {
EOF
type = map(object({
acronym = string
ip_address_names = optional(list(string))
certificates = optional(map(list(string)))
ip_address_names = optional(list(string), [])
certificates = optional(map(list(string)), {})
}))
validation {
condition = alltrue([for o in var.apps : length(o.acronym) <= 3])
Expand Down
2 changes: 1 addition & 1 deletion 2-multitenant/envs/nonproduction/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| apps | A map, where the key is the application name, defining the application configurations with the following properties:<br>- **acronym** (Required): A short identifier for the application with a maximum of 3 characters in length.<br>- **ip\_address\_names** (Optional): A list of IP address names associated with the application.<br>- **certificates** (Optional): A map of certificate names to a list of certificate values required by the application. | <pre>map(object({<br> acronym = string<br> ip_address_names = optional(list(string))<br> certificates = optional(map(list(string)))<br> }))</pre> | n/a | yes |
| apps | A map, where the key is the application name, defining the application configurations with the following properties:<br>- **acronym** (Required): A short identifier for the application with a maximum of 3 characters in length.<br>- **ip\_address\_names** (Optional): A list of IP address names associated with the application.<br>- **certificates** (Optional): A map of certificate names to a list of certificate values required by the application. | <pre>map(object({<br> acronym = string<br> ip_address_names = optional(list(string), [])<br> certificates = optional(map(list(string)), {})<br> }))</pre> | n/a | yes |
| envs | Environments | <pre>map(object({<br> billing_account = string<br> folder_id = string<br> network_project_id = string<br> network_self_link = string<br> org_id = string<br> subnets_self_links = list(string)<br> }))</pre> | n/a | yes |

## Outputs
Expand Down
4 changes: 2 additions & 2 deletions 2-multitenant/envs/nonproduction/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ variable "apps" {
EOF
type = map(object({
acronym = string
ip_address_names = optional(list(string))
certificates = optional(map(list(string)))
ip_address_names = optional(list(string), [])
certificates = optional(map(list(string)), {})
}))
validation {
condition = alltrue([for o in var.apps : length(o.acronym) <= 3])
Expand Down
2 changes: 1 addition & 1 deletion 2-multitenant/envs/production/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| apps | A map, where the key is the application name, defining the application configurations with the following properties:<br>- **acronym** (Required): A short identifier for the application with a maximum of 3 characters in length.<br>- **ip\_address\_names** (Optional): A list of IP address names associated with the application.<br>- **certificates** (Optional): A map of certificate names to a list of certificate values required by the application. | <pre>map(object({<br> acronym = string<br> ip_address_names = optional(list(string))<br> certificates = optional(map(list(string)))<br> }))</pre> | n/a | yes |
| apps | A map, where the key is the application name, defining the application configurations with the following properties:<br>- **acronym** (Required): A short identifier for the application with a maximum of 3 characters in length.<br>- **ip\_address\_names** (Optional): A list of IP address names associated with the application.<br>- **certificates** (Optional): A map of certificate names to a list of certificate values required by the application. | <pre>map(object({<br> acronym = string<br> ip_address_names = optional(list(string), [])<br> certificates = optional(map(list(string)), {})<br> }))</pre> | n/a | yes |
| envs | Environments | <pre>map(object({<br> billing_account = string<br> folder_id = string<br> network_project_id = string<br> network_self_link = string<br> org_id = string<br> subnets_self_links = list(string)<br> }))</pre> | n/a | yes |

## Outputs
Expand Down
4 changes: 2 additions & 2 deletions 2-multitenant/envs/production/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ variable "apps" {
EOF
type = map(object({
acronym = string
ip_address_names = optional(list(string))
certificates = optional(map(list(string)))
ip_address_names = optional(list(string), [])
certificates = optional(map(list(string)), {})
}))
validation {
condition = alltrue([for o in var.apps : length(o.acronym) <= 3])
Expand Down
10 changes: 2 additions & 8 deletions 2-multitenant/terraform.example.tfvars
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,7 @@ envs = {
}

apps = {
"my-app" : {
"ip_address_names" : [
"my-app-ip",
]
"certificates" : {
"my-app-cert" : ["my-domain"]
}
"acronym" = "ma",
"default-example" : {
"acronym" = "de",
}
}
6 changes: 3 additions & 3 deletions 3-fleetscope/terraform.example.tfvars
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
*/

namespace_ids = {
"cb-frontend" = "your-frontend-group@yourdomain.com",
"cb-accounts" = "your-accounts-group@yourdomain.com",
"cb-transactions" = "your-transactions-group@yourdomain.com"
"cb-frontend" = "your-frontend-group@yourdomain.com",
"cb-accounts" = "your-accounts-group@yourdomain.com",
"cb-ledger" = "your-ledger-group@yourdomain.com"
}

remote_state_bucket = "REMOTE_STATE_BUCKET"
11 changes: 7 additions & 4 deletions 4-appfactory/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ It will also create an Application Folder to group your admin projects under it,
```txt
.
└── fldr-common/
├── default-example/
│ ├── hello-world-admin
│ └── ...
├── cymbal-bank/
│ ├── accounts-userservice-admin
│ ├── accounts-contacts-admin
Expand Down Expand Up @@ -130,17 +133,17 @@ You can now deploy the into your common folder.
1. Run `init` and `plan` and review the output.

```bash
terraform -chdir=./apps/cymbal-bank init
terraform -chdir=./apps/cymbal-bank plan
terraform -chdir=./envs/shared init
terraform -chdir=./envs/shared plan
```

1. Run `apply`.

```bash
terraform -chdir=./apps/cymbal-bank apply
terraform -chdir=./envs/shared apply
```

If you receive any errors or made any changes to the Terraform config or `terraform.tfvars`, re-run `terraform -chdir=./apps/cymbal-bank plan` before you run `terraform -chdir=./apps/cymbal-bank apply`.
If you receive any errors or made any changes to the Terraform config or `terraform.tfvars`, re-run `terraform -chdir=./envs/shared plan` before you run `terraform -chdir=./envs/shared apply`.

## Troubleshooting

Expand Down
24 changes: 24 additions & 0 deletions 4-appfactory/envs/shared/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| billing\_account | Billing Account ID for application admin project resources. | `string` | n/a | yes |
| bucket\_force\_destroy | When deleting a bucket, this boolean option will delete all contained objects. If false, Terraform will fail to delete buckets which contain objects. | `bool` | `false` | no |
| bucket\_prefix | Name prefix to use for buckets created. | `string` | `"bkt"` | no |
| common\_folder\_id | Folder ID in which to create all application admin projects, must be prefixed with 'folders/' | `string` | n/a | yes |
| envs | Environments | <pre>map(object({<br> billing_account = string<br> folder_id = string<br> network_project_id = string<br> network_self_link = string<br> org_id = string<br> subnets_self_links = list(string)<br> }))</pre> | n/a | yes |
| location | Location for build buckets. | `string` | `"us-central1"` | no |
| org\_id | Google Cloud Organization ID. | `string` | n/a | yes |
| remote\_state\_bucket | Backend bucket to load Terraform Remote State Data from previous steps. | `string` | n/a | yes |
| tf\_apply\_branches | List of git branches configured to run terraform apply Cloud Build trigger. All other branches will run plan by default. | `list(string)` | <pre>[<br> "development",<br> "nonproduction",<br> "production"<br>]</pre> | no |
| trigger\_location | Location of for Cloud Build triggers created in the workspace. If using private pools should be the same location as the pool. | `string` | `"global"` | no |

## Outputs

| Name | Description |
|------|-------------|
| app-folders-ids | Pair of app-name and folder\_id |
| app-group | Description on the app-group components |

<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
22 changes: 22 additions & 0 deletions 4-appfactory/envs/shared/backend.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* 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.
*/

terraform {
backend "gcs" {
bucket = "UPDATE_ME"
prefix = "terraform/appfactory/shared"
}
}
41 changes: 41 additions & 0 deletions 4-appfactory/envs/shared/iam.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* 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 {
all_environments_cluster_service_accounts_iam_members = [for sa in local.cluster_service_accounts : "serviceAccount:${sa}"]

expanded_cluster_service_accounts = flatten([
for key in keys(local.app_services) : [
for sa in local.all_environments_cluster_service_accounts_iam_members : {
app_name = key
cluster_sa_member = sa
}
]
])
}

// Assign artifactregistry reader to cluster service accounts
// This allows docker images on application projects to be downloaded on the cluster
resource "google_folder_iam_member" "admin" {
// for each app folder, create permissions for dev/non prod and prod cluster service accounts
for_each = tomap({
for app_name_sa in local.expanded_cluster_service_accounts : "${app_name_sa.app_name}.${app_name_sa.cluster_sa_member}" => app_name_sa
})

folder = google_folder.app_folder[each.value.app_name].name
role = "roles/artifactregistry.reader"
member = each.value.cluster_sa_member
}
81 changes: 81 additions & 0 deletions 4-appfactory/envs/shared/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* 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 {
# Services in this list will receive dedicated projects for application-specific infrastructure, such as an app-specific database.
services_with_infra = []
app_services = {
// app name
"default-example" = [
// microservices names
"hello-world",
]
}

expanded_app_services = flatten([
for key, services in local.app_services : [
for service in services : {
app_name = key
# app acronym is defined in 2-multitenant variables
acronym = local.acronym[key]
service_name = service
create_env_projects = contains(local.services_with_infra, service)
}
]
])
}

// One folder per application, will group admin/service projects under it
resource "google_folder" "app_folder" {
for_each = local.app_services

display_name = each.key
parent = var.common_folder_id
}

module "components" {
source = "../../modules/app-group-baseline"

for_each = tomap({
for app_service in local.expanded_app_services : "${app_service.app_name}.${app_service.service_name}" => app_service
})

service_name = each.value.service_name
acronym = each.value.acronym
create_env_projects = each.value.create_env_projects

org_id = var.org_id
billing_account = var.billing_account
folder_id = google_folder.app_folder[each.value.app_name].folder_id
envs = var.envs
bucket_prefix = var.bucket_prefix
location = var.location
trigger_location = var.trigger_location
bucket_force_destroy = var.bucket_force_destroy
tf_apply_branches = var.tf_apply_branches

cloudbuild_sa_roles = {
development = {
roles = ["roles/owner"]
}
nonproduction = {
roles = ["roles/owner"]
}
production = {
roles = ["roles/owner"]
}
}
}
39 changes: 39 additions & 0 deletions 4-appfactory/envs/shared/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* 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 "app-group" {
description = "Description on the app-group components"
value = {
for k, value in module.components : k => {
app_env_project_ids : value.app_env_project_ids,
app_admin_project_id : value.app_admin_project_id,
app_infra_repository_name : value.app_infra_repository_name,
app_infra_repository_url : value.app_infra_repository_url,
app_cloudbuild_workspace_apply_trigger_id : value.app_cloudbuild_workspace_apply_trigger_id,
app_cloudbuild_workspace_plan_trigger_id : value.app_cloudbuild_workspace_plan_trigger_id,
app_cloudbuild_workspace_artifacts_bucket_name : value.app_cloudbuild_workspace_artifacts_bucket_name,
app_cloudbuild_workspace_logs_bucket_name : value.app_cloudbuild_workspace_logs_bucket_name,
app_cloudbuild_workspace_state_bucket_name : value.app_cloudbuild_workspace_state_bucket_name,
}
}
}

output "app-folders-ids" {
description = "Pair of app-name and folder_id"
value = {
for k, v in google_folder.app_folder : k => v.folder_id
}
}
33 changes: 33 additions & 0 deletions 4-appfactory/envs/shared/remote.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* 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.
*/

// These values are retrieved from the saved terraform state of the execution
// of previous step using the terraform_remote_state data source.
locals {
cluster_service_accounts = flatten([for state in data.terraform_remote_state.multitenant : state.outputs.cluster_service_accounts])
acronym = flatten([for state in data.terraform_remote_state.multitenant : state.outputs.acronyms])[0]
}

data "terraform_remote_state" "multitenant" {
for_each = var.envs

backend = "gcs"

config = {
bucket = var.remote_state_bucket
prefix = "terraform/multi_tenant/${each.key}"
}
}
Loading

0 comments on commit da26d9b

Please sign in to comment.