From df15e1d4a33e763946517d41d81c8caa3c175159 Mon Sep 17 00:00:00 2001 From: Ryan C Koch Date: Tue, 24 Jul 2018 21:43:21 -0500 Subject: [PATCH 1/5] adding new app engine support --- README.md | 1 + examples/app_engine/README.md | 13 +++++++ examples/app_engine/main.tf | 46 ++++++++++++++++++++++++ examples/app_engine/outputs.tf | 30 ++++++++++++++++ examples/app_engine/variables.tf | 27 ++++++++++++++ main.tf | 25 +++++++++++++ outputs.tf | 5 +++ test/integration/gcloud/integration.bats | 10 ++++-- test/integration/gcloud/run.sh | 3 ++ test/integration/gcloud/sample.sh | 1 + variables.tf | 6 ++++ 11 files changed, 164 insertions(+), 3 deletions(-) create mode 100755 examples/app_engine/README.md create mode 100755 examples/app_engine/main.tf create mode 100755 examples/app_engine/outputs.tf create mode 100755 examples/app_engine/variables.tf diff --git a/README.md b/README.md index 6b08d108..20c387de 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ In order to operate with the Service Account you must activate the following API - Cloud Billing API - cloudbilling.googleapis.com - Identity and Access Management API - iam.googleapis.com - Admin SDK - admin.googleapis.com +- Google App Engine Admin API - appengine.googleapis.com ### GSuite #### Admin diff --git a/examples/app_engine/README.md b/examples/app_engine/README.md new file mode 100755 index 00000000..1794fa3b --- /dev/null +++ b/examples/app_engine/README.md @@ -0,0 +1,13 @@ +# App Engine Project + +This example illustrates how to create a simple project with App Engine enabled. + +It will do the following: +- Create a project +- Active the Google App Engine Admin API on the new project +- Create a new App Engine app + +Expected variables: +- `admin_email` +- `organization_id` +- `billing_account` diff --git a/examples/app_engine/main.tf b/examples/app_engine/main.tf new file mode 100755 index 00000000..3959ce0c --- /dev/null +++ b/examples/app_engine/main.tf @@ -0,0 +1,46 @@ +/** + * Copyright 2018 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 { + credentials_file_path = "${path.module}/sa-key.json" +} + +/****************************************** + Provider configuration + *****************************************/ +provider "gsuite" { + credentials = "${file(local.credentials_file_path)}" + impersonated_user_email = "${var.admin_email}" +} + +module "project-factory" { + source = "../../" + random_project_id = "true" + name = "appeng-sample" + org_id = "${var.organization_id}" + billing_account = "${var.billing_account}" + credentials_path = "${local.credentials_file_path}" + + app_engine { + location_id = "us-central" + + feature_settings = [ + { + split_health_checks = true + }, + ] + } +} diff --git a/examples/app_engine/outputs.tf b/examples/app_engine/outputs.tf new file mode 100755 index 00000000..5cc8dede --- /dev/null +++ b/examples/app_engine/outputs.tf @@ -0,0 +1,30 @@ +/** + * Copyright 2018 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 "project_info_example" { + value = "${module.project-factory.project_id}" + description = "The ID of the created project" +} + +output "domain_example" { + value = "${module.project-factory.domain}" + description = "The organization's domain" +} + +output "app_engine_enabled_example" { + value = "${module.project-factory.app_engine_enabled}" + description = "Whether app engine is enabled" +} diff --git a/examples/app_engine/variables.tf b/examples/app_engine/variables.tf new file mode 100755 index 00000000..9abe8149 --- /dev/null +++ b/examples/app_engine/variables.tf @@ -0,0 +1,27 @@ +/** + * Copyright 2018 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 "admin_email" { + description = "Admin user email on Gsuite" +} + +variable "organization_id" { + description = "The organization id for the associated services" +} + +variable "billing_account" { + description = "The ID of the billing account to associate this project with" +} diff --git a/main.tf b/main.tf index c667a165..3b666291 100755 --- a/main.tf +++ b/main.tf @@ -37,6 +37,12 @@ locals { project_bucket_name = "${var.bucket_name != "" ? var.bucket_name : format("%s-state", var.name)}" create_bucket = "${var.bucket_project != "" ? "true" : "false"}" gsuite_group = "${var.group_name != "" || var.create_group}" + app_engine_enabled = "${length(keys(var.app_engine)) > 0 ? true : false}" + + app_engine_config = { + enabled = "${list(var.app_engine)}" + disabled = "${list()}" + } } /****************************************** @@ -76,6 +82,8 @@ resource "google_project" "project" { auto_create_network = "${var.auto_create_network}" labels = "${var.labels}" + + app_engine = "${local.app_engine_config["${local.app_engine_enabled ? "enabled" : "disabled"}"]}" } /****************************************** @@ -119,6 +127,23 @@ resource "null_resource" "delete_default_compute_service_account" { depends_on = ["google_project_service.project_services"] } +/****************************************** + Default app engine service account deletion + *****************************************/ +resource "null_resource" "delete_default_app_engine_service_account" { + count = "${local.app_engine_enabled ? 1 : 0}" + + provisioner "local-exec" { + command = "${path.module}/scripts/delete-service-account.sh ${local.project_id} ${var.credentials_path} ${local.project_id}@appspot.gserviceaccount.com" + } + + triggers { + app_engine_enabled = "${local.app_engine_enabled}" + } + + depends_on = ["google_project_service.project_services"] +} + /****************************************** Default Service Account configuration *****************************************/ diff --git a/outputs.tf b/outputs.tf index 374bcc8e..f8742375 100755 --- a/outputs.tf +++ b/outputs.tf @@ -62,3 +62,8 @@ output "project_bucket_url" { value = "${google_storage_bucket.project_bucket.*.url}" description = "Project's bucket url" } + +output "app_engine_enabled" { + value = "${local.app_engine_enabled}" + description = "Whether app engine is enabled" +} diff --git a/test/integration/gcloud/integration.bats b/test/integration/gcloud/integration.bats index f90e2c31..1ab7f0c2 100755 --- a/test/integration/gcloud/integration.bats +++ b/test/integration/gcloud/integration.bats @@ -49,14 +49,18 @@ [[ "${lines[5]}" = "projectId: $PROJECT_ID" ]] } -@test "Test the compute api must be activated" { +@test "Test the correct apis are activated" { export PROJECT_ID="$(terraform output project_info_example)" export GROUP_EMAIL="$(terraform output group_email_example)" run gcloud services list [ "$status" -eq 0 ] - [[ "${lines[1]}" = *"compute.googleapis.com"* ]] + [[ "${lines[2]}" = *"compute.googleapis.com"* ]] + + run gcloud services list + [ "$status" -eq 0 ] + [[ "${lines[1]}" = *"appengine.googleapis.com"* ]] } @test "Test that project has the shared vpc associated (host project)" { @@ -79,7 +83,7 @@ [[ "${lines[1]}" = " email: project-service-account@$PROJECT_ID.iam.gserviceaccount.com" ]] } -@test "Test project has not the default service account" { +@test "Test project has not the default compute or app engine service account" { export PROJECT_ID="$(terraform output project_info_example)" export GROUP_EMAIL="$(terraform output group_email_example)" diff --git a/test/integration/gcloud/run.sh b/test/integration/gcloud/run.sh index 65384884..50968396 100755 --- a/test/integration/gcloud/run.sh +++ b/test/integration/gcloud/run.sh @@ -72,6 +72,9 @@ module "project-factory" { sa_group = "$SA_GROUP" folder_id = "$FOLDER_ID" credentials_path = "\${local.credentials_file_path}" + app_engine { + location_id = "$LOCATION_ID" + } } EOF } diff --git a/test/integration/gcloud/sample.sh b/test/integration/gcloud/sample.sh index 2acab054..b8d1a7ab 100644 --- a/test/integration/gcloud/sample.sh +++ b/test/integration/gcloud/sample.sh @@ -35,4 +35,5 @@ export GROUP_NAME="test-group$RANDOM" export CREATE_GROUP="true" export FOLDER_ID="" export GROUP_ROLE="roles/editor" +export LOCATION_ID="us-central" export CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE=$CREDENTIALS_PATH diff --git a/variables.tf b/variables.tf index 0271e7d7..6ff6cf70 100755 --- a/variables.tf +++ b/variables.tf @@ -112,3 +112,9 @@ variable "auto_create_network" { description = "Create the default network" default = "false" } + +variable "app_engine" { + description = "A map for app engine configuration" + type = "map" + default = {} +} From a51eb1f65e51143af5b7dff92d9ce2fa268cfb38 Mon Sep 17 00:00:00 2001 From: Ryan C Koch Date: Wed, 25 Jul 2018 14:36:47 -0500 Subject: [PATCH 2/5] add test to verify app engine app created --- test/integration/gcloud/integration.bats | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/integration/gcloud/integration.bats b/test/integration/gcloud/integration.bats index 1ab7f0c2..82117afc 100755 --- a/test/integration/gcloud/integration.bats +++ b/test/integration/gcloud/integration.bats @@ -136,6 +136,15 @@ [[ "$output" = *"{u'role': u'roles/compute.networkUser', u'members': [u'group:$GROUP_EMAIL', u'serviceAccount:project-service-account@$PROJECT_ID.iam.gserviceaccount.com']}"* ]] } +@test "Test App Engine app created" { + + export PROJECT_ID="$(terraform output project_info_example)" + + run gcloud app describe --format="get(name)" + [ "$status" -eq 0 ] + [[ "$output" = "apps/$PROJECT_ID" ]] +} + # #################################### # # Terraform destroy test # # #################################### # From 06a594dd88b3f468c416bc2f722eff2ecc55c904 Mon Sep 17 00:00:00 2001 From: Ryan C Koch Date: Fri, 27 Jul 2018 14:10:21 -0500 Subject: [PATCH 3/5] don't delete default app engine service account --- main.tf | 17 ----------------- test/integration/gcloud/integration.bats | 19 +++++-------------- 2 files changed, 5 insertions(+), 31 deletions(-) diff --git a/main.tf b/main.tf index 3b666291..aa52c1f9 100755 --- a/main.tf +++ b/main.tf @@ -127,23 +127,6 @@ resource "null_resource" "delete_default_compute_service_account" { depends_on = ["google_project_service.project_services"] } -/****************************************** - Default app engine service account deletion - *****************************************/ -resource "null_resource" "delete_default_app_engine_service_account" { - count = "${local.app_engine_enabled ? 1 : 0}" - - provisioner "local-exec" { - command = "${path.module}/scripts/delete-service-account.sh ${local.project_id} ${var.credentials_path} ${local.project_id}@appspot.gserviceaccount.com" - } - - triggers { - app_engine_enabled = "${local.app_engine_enabled}" - } - - depends_on = ["google_project_service.project_services"] -} - /****************************************** Default Service Account configuration *****************************************/ diff --git a/test/integration/gcloud/integration.bats b/test/integration/gcloud/integration.bats index 82117afc..077e9530 100755 --- a/test/integration/gcloud/integration.bats +++ b/test/integration/gcloud/integration.bats @@ -73,25 +73,16 @@ [[ "${lines[1]}" = "$SHARED_VPC" ]] } -@test "Test project has the service account created" { +@test "Test project has only the expected service accounts" { export PROJECT_ID="$(terraform output project_info_example)" export GROUP_EMAIL="$(terraform output group_email_example)" - run gcloud iam service-accounts list --format=list + run gcloud iam service-accounts list --format="get(email)" [ "$status" -eq 0 ] - [[ "${lines[1]}" = " email: project-service-account@$PROJECT_ID.iam.gserviceaccount.com" ]] -} - -@test "Test project has not the default compute or app engine service account" { - - export PROJECT_ID="$(terraform output project_info_example)" - export GROUP_EMAIL="$(terraform output group_email_example)" - - run gcloud iam service-accounts list - [ "$status" -eq 0 ] - [[ "${lines[1]}" =~ project-service-account@$PROJECT_ID.iam.gserviceaccount.com ]] - [[ "${lines[2]}" = "" ]] + [[ "${lines[0]}" = "$PROJECT_ID@appspot.gserviceaccount.com" ]] + [[ "${lines[1]}" = "project-service-account@$PROJECT_ID.iam.gserviceaccount.com" ]] + [[ "${lines[3]}" = "" ]] } @test "Test Gsuite group $GROUP_EMAIL has role:$GROUP_ROLE on project" { From 36d03e42f8f38c988cf5c678854df5f150a2f01b Mon Sep 17 00:00:00 2001 From: Ryan C Koch Date: Mon, 30 Jul 2018 10:06:03 -0500 Subject: [PATCH 4/5] rename variable to REGION and set to us-east4 --- test/integration/gcloud/run.sh | 2 +- test/integration/gcloud/sample.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/gcloud/run.sh b/test/integration/gcloud/run.sh index 50968396..cca4c4c1 100755 --- a/test/integration/gcloud/run.sh +++ b/test/integration/gcloud/run.sh @@ -73,7 +73,7 @@ module "project-factory" { folder_id = "$FOLDER_ID" credentials_path = "\${local.credentials_file_path}" app_engine { - location_id = "$LOCATION_ID" + location_id = "$REGION" } } EOF diff --git a/test/integration/gcloud/sample.sh b/test/integration/gcloud/sample.sh index b8d1a7ab..8eb9d57f 100644 --- a/test/integration/gcloud/sample.sh +++ b/test/integration/gcloud/sample.sh @@ -35,5 +35,5 @@ export GROUP_NAME="test-group$RANDOM" export CREATE_GROUP="true" export FOLDER_ID="" export GROUP_ROLE="roles/editor" -export LOCATION_ID="us-central" +export REGION="us-east4" export CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE=$CREDENTIALS_PATH From 47787bba353ae533528c891ead71af930f5ea947 Mon Sep 17 00:00:00 2001 From: Ryan C Koch Date: Mon, 30 Jul 2018 12:51:56 -0500 Subject: [PATCH 5/5] better app_engine tests --- test/integration/gcloud/integration.bats | 32 +++++++++++++++++++++--- test/integration/gcloud/run.sh | 7 ++++++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/test/integration/gcloud/integration.bats b/test/integration/gcloud/integration.bats index 077e9530..163eac19 100755 --- a/test/integration/gcloud/integration.bats +++ b/test/integration/gcloud/integration.bats @@ -34,6 +34,24 @@ [[ "$output" =~ 0\ destroyed ]] } +@test "Terraform plan setting of App Engine settings" { + + run terraform plan + [ "$status" -eq 0 ] + [[ "$output" =~ 0\ to\ add ]] + [[ "$output" =~ 1\ to\ change ]] + [[ "$output" =~ 0\ to\ destroy ]] +} + +@test "Terraform apply" { + + run terraform apply -auto-approve + [ "$status" -eq 0 ] + [[ "$output" =~ 0\ added ]] + [[ "$output" =~ 1\ changed ]] + [[ "$output" =~ 0\ destroyed ]] +} + # #################################### # # gcloud tests # # #################################### # @@ -127,13 +145,19 @@ [[ "$output" = *"{u'role': u'roles/compute.networkUser', u'members': [u'group:$GROUP_EMAIL', u'serviceAccount:project-service-account@$PROJECT_ID.iam.gserviceaccount.com']}"* ]] } -@test "Test App Engine app created" { +@test "Test App Engine app created with the correct settings" { - export PROJECT_ID="$(terraform output project_info_example)" + PROJECT_ID="$(terraform output project_info_example)" + AUTH_DOMAIN="$(echo $GSUITE_ADMIN_ACCOUNT | cut -d '@' -f2)" - run gcloud app describe --format="get(name)" + run gcloud --project=${PROJECT_ID} app describe [ "$status" -eq 0 ] - [[ "$output" = "apps/$PROJECT_ID" ]] + [[ "${lines[0]}" = "authDomain: $AUTH_DOMAIN" ]] + [[ "${lines[4]}" = "featureSettings: {}" ]] + [[ "${lines[6]}" = "id: $PROJECT_ID}" ]] + [[ "${lines[7]}" = "name: apps/$PROJECT_ID" ]] + [[ "${lines[8]}" = "locationId: $REGION" ]] + [[ "${lines[9]}" = "servingStatus: SERVING" ]] } # #################################### # diff --git a/test/integration/gcloud/run.sh b/test/integration/gcloud/run.sh index cca4c4c1..07500b31 100755 --- a/test/integration/gcloud/run.sh +++ b/test/integration/gcloud/run.sh @@ -74,6 +74,13 @@ module "project-factory" { credentials_path = "\${local.credentials_file_path}" app_engine { location_id = "$REGION" + auth_domain = "$(echo $GSUITE_ADMIN_ACCOUNT | cut -d '@' -f2)" + + feature_settings = [ + { + split_health_checks = false + }, + ] } } EOF