This is an automated email from the ASF dual-hosted git repository.

damccorm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/beam.git


The following commit(s) were added to refs/heads/master by this push:
     new 5bd0c08260e Automation: Tourofbeam backend infrastructure deployment 
using cloudbuild (#26504)
5bd0c08260e is described below

commit 5bd0c08260e84d2a5d2fe820950511eae728c58d
Author: ruslan-ikhsan <[email protected]>
AuthorDate: Wed May 31 19:34:30 2023 +0500

    Automation: Tourofbeam backend infrastructure deployment using cloudbuild 
(#26504)
    
    * Initial commit for TOB Infra via Cloud Build
    
    * Configuring TF for Cloud Build
    
    * Update iam.tf
    
    * Updating TF scripts
    
    * Shell script updates
    
    * Refactoring TF scripts
    
    * Create README.md
    
    * Update README.md
    
    * Update inline.yaml
    
    * Refactoring TF scripts. Renamed yaml to .sh
    
    * Update README.md
    
    * ToB backend and infra triggers
    
    * fixing var issues
    
    * fixing attribure name
    
    * improving readability
    
    * improving readability
    
    * fixing variable names and readability
    
    * fixing variables and env
    
    * adding missing variable
    
    * formatting
    
    * line anding formatting
    
    * fixing test
    
    * fixing gradle to deploy with cloud build
    
    * fixing gradle error
    
    * fix gradle firebase command
    
    * multitenancy for frontend deployment
    
    * adding hosting target
    
    * correct resource dependency
    
    * fixing secret
    
    * missing secret role
    
    * fixing gradle hosting task
    
    * fix gradle errors
    
    * fixing flutter link
    
    * fixing dependency for gradle tasks
    
    * test fix for flutter
    
    * adding conditional logic for hosting site creation
    
    * wrong variable name fixed
    
    * fixing flutter build
    
    * fix regexp
    
    * fix regexp
    
    * fix escape for kotlin
    
    * try another regex
    
    * regex fix
    
    * fixing flutter commands
    
    * fixing flutter
    
    * update hosting target
    
    * fix readme whitespace
    
    * removing unused sa and vars
    
    * missing role to write logs
    
    * fixing role to write to buckets
    
    * updating cb deployment Readme
    
    * switch to ADC authentication for firebase
    
    * disabling firebase token env
    
    * removing firebase token references
    
    * removing incorrect defaults
    
    * removing unused vars
    
    * meaningful name for var
    
    * fixing readme
    
    * release flutter build
    
    * improving ToB CD
    
    * initial deployment of LM
    
    * syntax error fixed
    
    * fixing prod url backend fallback
    
    * fixing LM CD trigger
    
    * fixing git error
    
    * remove html renderer
    
    * adding LM deployment to cd
    
    * update flutter version
    
    * minor formatting
    
    * restoring condition
    
    * updating README
    
    * missing Playground README link added
    
    ---------
    
    Co-authored-by: Oleh Borysevych <[email protected]>
---
 learning/tour-of-beam/backend/internal/sdk.go      |   4 +-
 learning/tour-of-beam/backend/internal/sdk_test.go |   1 -
 .../backend/samples/api/get_sdk_list.json          |   3 +-
 learning/tour-of-beam/cloudbuild/01.setup/iam.tf   |  76 ++++++++
 .../output.tf => cloudbuild/01.setup/provider.tf}  |   7 +-
 .../01.setup/services.tf}                          |  13 +-
 .../01.setup/terraform.tf}                         |  13 +-
 .../setup => cloudbuild/01.setup}/variables.tf     |  20 +-
 .../locals.tf => cloudbuild/02.builders/data.tf}   |  33 ++--
 .../tour-of-beam/cloudbuild/02.builders/locals.tf  | 101 ++++++++++
 .../02.builders/provider.tf}                       |   7 +-
 .../02.builders/terraform.tf}                      |  14 +-
 .../cloudbuild/02.builders/triggers.tf             | 217 +++++++++++++++++++++
 .../cloudbuild/02.builders/variables.tf            | 132 +++++++++++++
 learning/tour-of-beam/cloudbuild/README.md         | 153 +++++++++++++++
 .../cloudbuild/scripts/tob_deploy_infra_backend.sh |  89 +++++++++
 .../tour-of-beam/cloudbuild/scripts/tob_lm_cd.sh   |  42 ++++
 learning/tour-of-beam/terraform/README.md          |  25 +--
 learning/tour-of-beam/terraform/build.gradle.kts   |  95 ++++++---
 .../tour-of-beam/terraform/cloud_functions/main.tf |   2 +-
 .../terraform/cloud_functions/variables.tf         |   2 +-
 .../terraform/functions_buckets/locals.tf          |  11 +-
 .../terraform/functions_buckets/variables.tf       |   4 +
 learning/tour-of-beam/terraform/main.tf            |   5 +-
 learning/tour-of-beam/terraform/setup/iam.tf       |  12 +-
 learning/tour-of-beam/terraform/setup/locals.tf    |   9 +-
 learning/tour-of-beam/terraform/setup/output.tf    |   2 +-
 learning/tour-of-beam/terraform/setup/variables.tf |   6 +-
 learning/tour-of-beam/terraform/variables.tf       |   9 +-
 .../lib/src/constants/backend_urls.dart            |   2 +-
 30 files changed, 978 insertions(+), 131 deletions(-)

diff --git a/learning/tour-of-beam/backend/internal/sdk.go 
b/learning/tour-of-beam/backend/internal/sdk.go
index f088eb3b35e..cf14600ef6c 100644
--- a/learning/tour-of-beam/backend/internal/sdk.go
+++ b/learning/tour-of-beam/backend/internal/sdk.go
@@ -79,8 +79,8 @@ func ParseSdk(s string) Sdk {
 }
 
 func MakeSdkList() SdkList {
-       sdks := make([]SdkItem, 0, 4)
-       for _, sdk := range []Sdk{SDK_JAVA, SDK_PYTHON, SDK_GO, SDK_SCIO} {
+       sdks := make([]SdkItem, 0, 3)
+       for _, sdk := range []Sdk{SDK_JAVA, SDK_PYTHON, SDK_GO} {
                sdks = append(sdks, SdkItem{Id: sdk.String(), Title: 
sdk.Title()})
        }
        return SdkList{Sdks: sdks}
diff --git a/learning/tour-of-beam/backend/internal/sdk_test.go 
b/learning/tour-of-beam/backend/internal/sdk_test.go
index 1352893c237..3d33aceed74 100644
--- a/learning/tour-of-beam/backend/internal/sdk_test.go
+++ b/learning/tour-of-beam/backend/internal/sdk_test.go
@@ -76,7 +76,6 @@ func TestSdkList(t *testing.T) {
                        {"java", "Java"},
                        {"python", "Python"},
                        {"go", "Go"},
-                       {"scio", "SCIO"},
                },
        }, MakeSdkList())
 }
diff --git a/learning/tour-of-beam/backend/samples/api/get_sdk_list.json 
b/learning/tour-of-beam/backend/samples/api/get_sdk_list.json
index b24d25f1f9e..7c91da96717 100644
--- a/learning/tour-of-beam/backend/samples/api/get_sdk_list.json
+++ b/learning/tour-of-beam/backend/samples/api/get_sdk_list.json
@@ -2,7 +2,6 @@
   "sdks" : [
    {"id": "java", "title": "Java"},
    {"id": "python", "title": "Python"},
-   {"id": "go", "title": "Go"},
-   {"id": "scio", "title": "SCIO"}
+   {"id": "go", "title": "Go"}
   ]
 }
\ No newline at end of file
diff --git a/learning/tour-of-beam/cloudbuild/01.setup/iam.tf 
b/learning/tour-of-beam/cloudbuild/01.setup/iam.tf
new file mode 100644
index 00000000000..3f619dc7073
--- /dev/null
+++ b/learning/tour-of-beam/cloudbuild/01.setup/iam.tf
@@ -0,0 +1,76 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+
+# Create cloud build service account
+resource "google_service_account" "tourofbeam_deploy_sa" {
+  account_id   = var.tourofbeam_deploy_sa
+  description  = "The service account to be used by cloud build to deploy Tour 
of Beam backend"
+}
+
+resource "google_service_account" "tourofbeam_ci_sa" {
+  account_id   = var.tourofbeam_ci_sa
+  description  = "The service account to be used by cloud build to run CI 
checks for Tour of Beam backend"
+}
+
+resource "google_service_account" "tourofbeam_cd_sa" {
+  account_id   = var.tourofbeam_cd_sa
+  description  = "The service account to be used by cloud build to run CD 
checks for Tour of Beam backend"
+}
+
+# Assign IAM roles to cloud build service account
+resource "google_project_iam_member" "tourofbeam_backend_deployer_roles" {
+  for_each = toset([
+    "roles/datastore.indexAdmin",
+    "roles/datastore.user",
+    "roles/iam.serviceAccountCreator",
+    "roles/iam.serviceAccountUser",
+    "roles/serviceusage.serviceUsageAdmin",
+    "roles/storage.admin",
+    "roles/firebase.admin",
+    "roles/container.clusterViewer",
+    "roles/logging.logWriter",
+    "roles/cloudfunctions.admin"
+  ])
+  role    = each.key
+  member  = 
"serviceAccount:${google_service_account.tourofbeam_deploy_sa.email}"
+  project = var.project_id
+}
+
+resource "google_project_iam_member" "tourofbeam_ci_sa_roles" {
+  for_each = toset([
+    "roles/secretmanager.secretAccessor",
+    "roles/storage.insightsCollectorService",
+    "roles/storage.objectAdmin"
+  ])
+  role    = each.key
+  member  = "serviceAccount:${google_service_account.tourofbeam_ci_sa.email}"
+  project = var.project_id
+}
+
+resource "google_project_iam_member" "tourofbeam_cd_sa_roles" {
+  for_each = toset([
+    "roles/datastore.user",
+    "roles/storage.objectAdmin",
+    "roles/secretmanager.secretAccessor",
+    "roles/storage.insightsCollectorService"
+
+  ])
+  role    = each.key
+  member  = "serviceAccount:${google_service_account.tourofbeam_cd_sa.email}"
+  project = var.project_id
+}
+
diff --git a/learning/tour-of-beam/terraform/setup/output.tf 
b/learning/tour-of-beam/cloudbuild/01.setup/provider.tf
similarity index 83%
copy from learning/tour-of-beam/terraform/setup/output.tf
copy to learning/tour-of-beam/cloudbuild/01.setup/provider.tf
index bd069f1ed8d..88041d610b3 100644
--- a/learning/tour-of-beam/terraform/setup/output.tf
+++ b/learning/tour-of-beam/cloudbuild/01.setup/provider.tf
@@ -15,7 +15,6 @@
 # specific language governing permissions and limitations
 # under the License.
 
-# Output used to assign service account to cloud functions
-output "service-account-email" {
-  value = google_service_account.cloud_function_sa.email
-}
\ No newline at end of file
+provider "google" {
+  project = var.project_id
+}
diff --git a/learning/tour-of-beam/terraform/functions_buckets/variables.tf 
b/learning/tour-of-beam/cloudbuild/01.setup/services.tf
similarity index 75%
copy from learning/tour-of-beam/terraform/functions_buckets/variables.tf
copy to learning/tour-of-beam/cloudbuild/01.setup/services.tf
index f1cb931f2a2..635b2122f23 100644
--- a/learning/tour-of-beam/terraform/functions_buckets/variables.tf
+++ b/learning/tour-of-beam/cloudbuild/01.setup/services.tf
@@ -15,7 +15,14 @@
 # specific language governing permissions and limitations
 # under the License.
 
-# GCP region where GCS bucket will be created
-variable "region" {
-  description = "The GCP region where GCS bucket will be created (For Cloud 
Functions source code)"
+# Enable required GCP APIs
+resource "google_project_service" "required_services" {
+  for_each  = toset([
+    "cloudresourcemanager",
+    "iam",
+    "cloudbuild",
+    "cloudfunctions"
+  ])
+  service   = "${each.key}.googleapis.com"
+  disable_on_destroy = false
 }
diff --git a/learning/tour-of-beam/terraform/functions_buckets/variables.tf 
b/learning/tour-of-beam/cloudbuild/01.setup/terraform.tf
similarity index 82%
copy from learning/tour-of-beam/terraform/functions_buckets/variables.tf
copy to learning/tour-of-beam/cloudbuild/01.setup/terraform.tf
index f1cb931f2a2..0c9748b8666 100644
--- a/learning/tour-of-beam/terraform/functions_buckets/variables.tf
+++ b/learning/tour-of-beam/cloudbuild/01.setup/terraform.tf
@@ -15,7 +15,14 @@
 # specific language governing permissions and limitations
 # under the License.
 
-# GCP region where GCS bucket will be created
-variable "region" {
-  description = "The GCP region where GCS bucket will be created (For Cloud 
Functions source code)"
+terraform {
+  backend "gcs" {
+    prefix = "01.setup"
+  }
+  required_providers {
+    google = {
+      source  = "hashicorp/google"
+      version = "~> 4.62.0"
+    }
+  }
 }
diff --git a/learning/tour-of-beam/terraform/setup/variables.tf 
b/learning/tour-of-beam/cloudbuild/01.setup/variables.tf
similarity index 61%
copy from learning/tour-of-beam/terraform/setup/variables.tf
copy to learning/tour-of-beam/cloudbuild/01.setup/variables.tf
index dcb6f54808b..c1dc5f64c6d 100644
--- a/learning/tour-of-beam/terraform/setup/variables.tf
+++ b/learning/tour-of-beam/cloudbuild/01.setup/variables.tf
@@ -15,13 +15,23 @@
 # specific language governing permissions and limitations
 # under the License.
 
-# Required and not inferred from the provider argument.
-# Generated by command (gcloud config get-value project) in Kotlin Gradle 
script
 variable "project_id" {
   description = "The ID of the Google Cloud project within which resources are 
provisioned"
 }
 
-# This variable is generated by command (gcloud config get-value core/account) 
in Kotlin Gradle script
-variable "gcloud_init_account" {
-  description = "User Account ID logged in with gcloud init command (e.g. 
[email protected])"
+variable "tourofbeam_deploy_sa" {
+  description = "The ID of the cloud build service account responsible for 
deploying the Tour of Beam"
+  default = "tourofbeam-cloudbuild-deploy-sa"
 }
+
+variable "tourofbeam_ci_sa" {
+  description = "The ID of the cloud build service account responsible for 
running Tour of Beam CI checks and scripts"
+  default = "tourofbeam-cloudbuild-ci-sa"
+}
+
+variable "tourofbeam_cd_sa" {
+  description = "The ID of the cloud build service account responsible for 
running Tour of Beam CD checks and scripts"
+  default = "tourofbeam-cloudbuild-cd-sa"
+}
+
+
diff --git a/learning/tour-of-beam/terraform/setup/locals.tf 
b/learning/tour-of-beam/cloudbuild/02.builders/data.tf
similarity index 51%
copy from learning/tour-of-beam/terraform/setup/locals.tf
copy to learning/tour-of-beam/cloudbuild/02.builders/data.tf
index 9817b8f2821..340ad972d42 100644
--- a/learning/tour-of-beam/terraform/setup/locals.tf
+++ b/learning/tour-of-beam/cloudbuild/02.builders/data.tf
@@ -15,20 +15,27 @@
 # specific language governing permissions and limitations
 # under the License.
 
-# Local value to store generated Cloud Functions' Service account name
+# Takes data of service account created for cloud build trigger
+ data "google_service_account" "tourofbeam_deployer" {
+   account_id   = var.tourofbeam_deploy_sa
+ }
+ 
+ data "google_service_account" "tourofbeam_ci_runner" {
+    account_id   = var.tourofbeam_ci_sa
+  }
 
-resource "random_string" "id" {
-  length = 4
-  upper = false
-  special = false
-}
+  data "google_service_account" "tourofbeam_cd_runner" {
+    account_id   = var.tourofbeam_cd_sa
+  }
 
-variable "resource_name_prefix" {
-  type = string
-  description = "The resource name prefix applied to all resource naming for 
the application"
-  default = "tour-of-beam"
+# Takes data of secretst created for cloud build trigger
+  data "google_secret_manager_secret_version" 
"secret_webhook_cloudbuild_trigger_cicd_data" {
+  secret      = var.webhook_trigger_secret_id
+  version     = "latest"
 }
 
-locals {
-  cloudfunctions_service_account = 
"${var.resource_name_prefix}-cf-sa-${random_string.id.result}"
-}
\ No newline at end of file
+  data "google_secret_manager_secret_version" "secret_gh_pat_cloudbuild_data" {
+  secret      = var.gh_pat_secret_id
+  version     = "latest"
+}
+ 
\ No newline at end of file
diff --git a/learning/tour-of-beam/cloudbuild/02.builders/locals.tf 
b/learning/tour-of-beam/cloudbuild/02.builders/locals.tf
new file mode 100644
index 00000000000..946641c0beb
--- /dev/null
+++ b/learning/tour-of-beam/cloudbuild/02.builders/locals.tf
@@ -0,0 +1,101 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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 { 
+   cloudbuild_init_environment = [
+   "BRANCH_NAME=$_BRANCH_NAME",
+   "REPO_NAME=$_REPO_NAME" ,
+   "PG_REGION=$_PG_REGION",
+   "DNS_NAME=$_DNS_NAME",
+   "WEB_APP_ID=$_WEB_APP_ID",
+   "PG_GKE_ZONE=$_PG_GKE_ZONE",
+   "PG_GKE_NAME=$_PG_GKE_NAME",
+   "PROJECT_ID=$PROJECT_ID",
+   "STATE_BUCKET=$_STATE_BUCKET",
+
+   # Learning material 
+   "DATASTORE_PROJECT_ID=$PROJECT_ID",
+   "DATASTORE_NAMESPACE=$_PG_DATASTORE_NAMESPACE",
+   "TOB_LEARNING_ROOT=$_TOB_LEARNING_ROOT",
+
+   # Terraform variables
+   "TF_VAR_environment=$_ENVIRONMENT_NAME",
+   "TF_VAR_region=$_PG_REGION",
+   "TF_VAR_project_id=$PROJECT_ID",
+   "TF_VAR_datastore_namespace=$_PG_DATASTORE_NAMESPACE",
+   ]
+
+   cloudbuild_cd_environment = [ 
+   "PROJECT_ID=$PROJECT_ID",
+   "DATASTORE_NAMESPACE=$_DATASTORE_NAMESPACE",
+   "DNS_NAME=$_DNS_NAME",
+   "PR_URL=$_PR_URL",
+   "TARGET_PR_REPO_BRANCH=$_TARGET_PR_REPO_BRANCH",
+   "PR_TYPE=$_PR_TYPE",
+   "MERGE_STATUS=$_MERGE_STATUS",
+   "MERGE_COMMIT=$_MERGE_COMMIT",
+   "ORIGIN=$_ORIGIN",
+   "SUBDIRS=$_SUBDIRS",
+   "SDKS=$_SDKS",
+   "BEAM_CONCURRENCY=$_BEAM_CONCURRENCY",
+   "PR_COMMIT=$_PR_COMMIT",
+   
"CD_SCRIPT_PATH=beam/playground/infrastructure/cloudbuild/playground_cd_examples.sh",
+   "FORCE_CD=false",
+   # Learning material 
+   "DATASTORE_PROJECT_ID=$PROJECT_ID",
+   "DATASTORE_NAMESPACE=$_DATASTORE_NAMESPACE",
+   "TOB_LEARNING_ROOT=$_TOB_LEARNING_ROOT",
+    ]
+
+   cloudbuild_cd_environment_manual = [ 
+   "PROJECT_ID=$PROJECT_ID",
+   "DNS_NAME=$_DNS_NAME",
+   "PR_URL=URL",
+   "TARGET_PR_REPO_BRANCH=apache:master",
+   "PR_TYPE=closed",
+   "MERGE_STATUS=true",
+   "MERGE_COMMIT=$_MERGE_COMMIT",
+   "ORIGIN=$_ORIGIN",
+   "SUBDIRS=$_SUBDIRS",
+   "SDKS=$_SDKS",
+   "BEAM_CONCURRENCY=$_BEAM_CONCURRENCY",
+   "PR_COMMIT=$_PR_COMMIT",
+   
"CD_SCRIPT_PATH=beam/playground/infrastructure/cloudbuild/playground_cd_examples.sh",
+   "FORCE_CD=true",
+   # Learning material 
+   "DATASTORE_PROJECT_ID=$PROJECT_ID",
+   "DATASTORE_NAMESPACE=$_DATASTORE_NAMESPACE",
+   "TOB_LEARNING_ROOT=$_TOB_LEARNING_ROOT",
+    ]
+
+   cloudbuild_ci_environment = [ 
+   "PROJECT_ID=$PROJECT_ID",
+   "PR_BRANCH=$_PR_BRANCH",
+   "PR_URL=$_PR_URL",
+   "PR_TYPE=$_PR_TYPE",
+   "PR_COMMIT=$_PR_COMMIT",
+   "PR_NUMBER=$_PR_NUMBER",
+   
"CI_SCRIPT_PATH=beam/playground/infrastructure/cloudbuild/playground_ci_examples.sh",
+   "PUBLIC_BUCKET=$_PUBLIC_BUCKET",
+   "PUBLIC_LOG=$_PUBLIC_LOG",
+   "PUBLIC_LOG_URL=$_PUBLIC_LOG_URL",
+   "PUBLIC_LOG_LOCAL=$_PUBLIC_LOG_LOCAL",
+   "FORK_REPO=$_FORK_REPO",
+   "BASE_REF=$_BASE_REF",
+   "BEAM_VERSION=$_BEAM_VERSION"
+   ]
+}
diff --git a/learning/tour-of-beam/terraform/setup/output.tf 
b/learning/tour-of-beam/cloudbuild/02.builders/provider.tf
similarity index 83%
copy from learning/tour-of-beam/terraform/setup/output.tf
copy to learning/tour-of-beam/cloudbuild/02.builders/provider.tf
index bd069f1ed8d..88041d610b3 100644
--- a/learning/tour-of-beam/terraform/setup/output.tf
+++ b/learning/tour-of-beam/cloudbuild/02.builders/provider.tf
@@ -15,7 +15,6 @@
 # specific language governing permissions and limitations
 # under the License.
 
-# Output used to assign service account to cloud functions
-output "service-account-email" {
-  value = google_service_account.cloud_function_sa.email
-}
\ No newline at end of file
+provider "google" {
+  project = var.project_id
+}
diff --git a/learning/tour-of-beam/terraform/functions_buckets/variables.tf 
b/learning/tour-of-beam/cloudbuild/02.builders/terraform.tf
similarity index 77%
copy from learning/tour-of-beam/terraform/functions_buckets/variables.tf
copy to learning/tour-of-beam/cloudbuild/02.builders/terraform.tf
index f1cb931f2a2..e5ba50c7a15 100644
--- a/learning/tour-of-beam/terraform/functions_buckets/variables.tf
+++ b/learning/tour-of-beam/cloudbuild/02.builders/terraform.tf
@@ -15,7 +15,15 @@
 # specific language governing permissions and limitations
 # under the License.
 
-# GCP region where GCS bucket will be created
-variable "region" {
-  description = "The GCP region where GCS bucket will be created (For Cloud 
Functions source code)"
+# Using 4.62.0 provider to run script block in build
+terraform {
+  backend "gcs" {
+    prefix = "02.builders"
+  }
+  required_providers {
+    google = {
+      source  = "hashicorp/google"
+      version = "~> 4.62.0"
+    }
+  }
 }
diff --git a/learning/tour-of-beam/cloudbuild/02.builders/triggers.tf 
b/learning/tour-of-beam/cloudbuild/02.builders/triggers.tf
new file mode 100644
index 00000000000..00c1c7a6c87
--- /dev/null
+++ b/learning/tour-of-beam/cloudbuild/02.builders/triggers.tf
@@ -0,0 +1,217 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+
+# This creates cloud build trigger for deploying Tour of Beam backend
+resource "google_cloudbuild_trigger" "tourofbeam_deployment_trigger" {
+  name     = var.tourofbeam_deploy_trigger_name
+
+  description = "Trigger used to deploy Tour of Beam infrastructure and 
backend"
+
+  source_to_build {
+    uri       = var.trigger_source_repo
+    ref       = "refs/heads/${var.trigger_source_branch}"
+    repo_type = "GITHUB"
+  }
+
+  build {
+    timeout = "7200s"
+    options {
+      machine_type = var.cloudbuild_machine_type
+      logging      = "GCS_ONLY"
+    }
+
+    logs_bucket = "gs://${var.tourofbeam_cb_private_bucket}"
+
+    step {
+      id = "Deploy Tour of Beam"
+      script = file("../scripts/tob_deploy_infra_backend.sh")
+      name = "ubuntu"
+      env = local.cloudbuild_init_environment
+    }
+  }
+  
+  substitutions = {
+    _PG_REGION              = var.pg_region
+    _PG_GKE_ZONE            = var.pg_gke_zone
+    _PG_GKE_NAME            = var.pg_gke_name
+    _PG_DATASTORE_NAMESPACE = var.pg_datastore_namespace
+    _DNS_NAME               = var.playground_dns_name
+    _WEB_APP_ID             = var.web_app_id
+    _STATE_BUCKET           = var.tourofbeam_deployment_state_bucket
+    _ENVIRONMENT_NAME       = var.environment_name
+    _TOB_LEARNING_ROOT      = var.tourofbeam_learning_root
+    _BRANCH_NAME            = var.terraform_source_branch
+    _REPO_NAME              = var.terraform_source_repo
+    }
+
+  service_account = data.google_service_account.tourofbeam_deployer.id
+}
+
+resource "google_cloudbuild_trigger" "tourofbeam_ci_trigger" {
+  name    = var.tourofbeam_ci_trigger_name
+  project = var.project_id
+
+  description = "Validate changed examples and set commit status messages in 
GitHub"
+
+  service_account = data.google_service_account.tourofbeam_ci_runner.id
+
+  webhook_config {
+    secret = 
data.google_secret_manager_secret_version.secret_webhook_cloudbuild_trigger_cicd_data.id
+  }
+
+  build {
+    timeout = "7200s"
+    options {
+      machine_type = var.cloudbuild_machine_type
+      logging      = "GCS_ONLY"
+    }
+    logs_bucket = "gs://${var.tourofbeam_cb_private_bucket}" 
+    step {
+      id     = "Run CI"
+      script = 
file("../../../../playground/infrastructure/cloudbuild/cloudbuild_playground_ci_examples.sh")
+      name   = "ubuntu"
+      env    = local.cloudbuild_ci_environment
+      secret_env = ["PAT"]
+    }
+    available_secrets {
+      secret_manager {
+        env          = "PAT"
+        version_name = 
"${data.google_secret_manager_secret_version.secret_gh_pat_cloudbuild_data.secret}/versions/latest"
+      }
+    }
+    substitutions = {
+      _PR_BRANCH            = "$(body.pull_request.head.ref)"
+      _PR_URL               = "$(body.pull_request._links.html.href)"
+      _PR_TYPE              = "$(body.action)"
+      _PR_COMMIT            = "$(body.pull_request.head.sha)"
+      _PR_NUMBER            = "$(body.number)"
+      _FORK_REPO            = "$(body.pull_request.head.repo.full_name)"
+      _BASE_REF             = "$(body.pull_request.base.ref)"
+      _PUBLIC_LOG_LOCAL: "/tmp/$${_PUBLIC_LOG}"
+      _PUBLIC_LOG: "CI_PR$${_PR_NUMBER}_$${_PR_COMMIT}_$${BUILD_ID}.txt"
+      _PUBLIC_BUCKET: "${var.tourofbeam_cb_public_bucket}"
+      _PUBLIC_LOG_URL: 
"https://storage.googleapis.com/$${_PUBLIC_BUCKET}/$${_PUBLIC_LOG}";
+    }
+  }
+
+  substitutions = {
+    _BEAM_VERSION             = "2.47.0"
+  }
+
+}
+
+resource "google_cloudbuild_trigger" "tourofbeam_cd_trigger" {
+  name    = var.tourofbeam_cd_trigger_name
+  project = var.project_id
+
+  description = "Automatically update examples for an existing Tour of Beam 
environment"
+
+  service_account = data.google_service_account.tourofbeam_cd_runner.id
+
+  webhook_config {
+    secret = 
data.google_secret_manager_secret_version.secret_webhook_cloudbuild_trigger_cicd_data.id
+  }
+
+  build {
+    timeout = "7200s"
+    options {
+      machine_type = var.cloudbuild_machine_type
+      logging      = "GCS_ONLY"
+    }
+    logs_bucket = "gs://${var.tourofbeam_cb_private_bucket}"
+    step {
+      id     = "Run Learning Materials CD"
+      script = file("../scripts/tob_lm_cd.sh")
+      name   = "ubuntu"
+      env    = local.cloudbuild_cd_environment_manual
+    }
+    step {
+      id     = "Run Example CD"
+      script = 
file("../../../../playground/infrastructure/cloudbuild/cloudbuild_playground_cd_examples.sh")
+      name   = "ubuntu"
+      env    = local.cloudbuild_cd_environment
+    }
+    substitutions = {
+      _PR_URL                = "$(body.pull_request._links.html.href)"
+      _TARGET_PR_REPO_BRANCH = "$(body.pull_request.base.label)"
+      _PR_TYPE               = "$(body.action)"
+      _MERGE_STATUS          = "$(body.pull_request.merged)"
+      _MERGE_COMMIT          = "$(body.pull_request.merge_commit_sha)"
+    }
+  }
+
+  substitutions = {
+    _DNS_NAME              = var.playground_dns_name
+    _DATASTORE_NAMESPACE   = var.pg_datastore_namespace
+    _ORIGIN                = "TB_EXAMPLES"
+    _TOB_LEARNING_ROOT     = var.tourofbeam_learning_root
+    _SDKS                  = "java python go"
+    _SUBDIRS               = "./learning/tour-of-beam/learning-content"
+    _BEAM_CONCURRENCY      = "4"
+  }
+
+}
+
+resource "google_cloudbuild_trigger" "tourofbeam_cd_manual_trigger" {
+  name    = "${var.tourofbeam_cd_trigger_name}-manual"
+  project = var.project_id
+
+  description = "Manually update examples for an existing Tour of Beam 
environment"
+
+  service_account = data.google_service_account.tourofbeam_cd_runner.id
+
+  source_to_build {
+    uri       = var.trigger_source_repo
+    ref       = "refs/heads/${var.trigger_source_branch}"
+    repo_type = "GITHUB"
+  }
+
+  build {
+    timeout = "7200s"
+    options {
+      machine_type = var.cloudbuild_machine_type
+      logging      = "GCS_ONLY"
+    }
+    logs_bucket = "gs://${var.tourofbeam_cb_private_bucket}" 
+
+    step {
+      id     = "Run Learning Materials CD"
+      script = file("../scripts/tob_lm_cd.sh")
+      name   = "ubuntu"
+      env    = local.cloudbuild_cd_environment_manual
+    }
+    step {
+      id     = "Run Example CD"
+      script = 
file("../../../../playground/infrastructure/cloudbuild/cloudbuild_playground_cd_examples.sh")
+      name   = "ubuntu"
+      env    = local.cloudbuild_cd_environment_manual
+    }
+  }
+
+  substitutions = {
+    _DNS_NAME            = var.playground_dns_name
+    _DATASTORE_NAMESPACE = var.pg_datastore_namespace
+    _MERGE_COMMIT        = "master"
+    _ORIGIN              = "TB_EXAMPLES"
+    _TOB_LEARNING_ROOT   = var.tourofbeam_learning_root
+    _SDKS                = "java python go"
+    _SUBDIRS             = "./learning/tour-of-beam/learning-content"
+    _BEAM_CONCURRENCY    = "4"
+  }
+
+}
+
diff --git a/learning/tour-of-beam/cloudbuild/02.builders/variables.tf 
b/learning/tour-of-beam/cloudbuild/02.builders/variables.tf
new file mode 100644
index 00000000000..4b687ab52d0
--- /dev/null
+++ b/learning/tour-of-beam/cloudbuild/02.builders/variables.tf
@@ -0,0 +1,132 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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 "project_id" {
+    description = "GCP project id where resources will be created"
+}
+
+# Trigger service account variables
+variable "tourofbeam_deploy_sa" {
+    description = "Service account name to be created and used by cloud build 
deployer"
+}
+
+variable "tourofbeam_ci_sa" {
+    description = "Service account name to be created and used by cloud build 
CI"
+}
+
+variable "tourofbeam_cd_sa" {
+    description = "Service account name to be created and used by cloud build 
CD"
+}
+
+# Playground variables
+variable "environment_name" {
+    description = "Environment name for Tour of Beam backend (e.g. prod, 
staging). To support multi-environment on same GCP project"
+}
+
+variable "playground_dns_name" {
+  description = "The DNS A-record name for Playground website"
+  default = "fqdn.playground.zone"
+}
+
+variable "pg_region" {
+    description = "Existing Beam Playground's region (e.g: us-west1)"
+}
+
+variable "pg_gke_zone" {
+    description = "Existing Beam Playground GKE cluster's zone (e.g. 
us-west1-b)"
+}
+
+variable "pg_gke_name" {
+    description = "Existing Beam Playground GKE cluster's name"
+}
+
+variable "pg_datastore_namespace" {
+    description = "Existing Beam Playground's datastore namespace"
+}
+
+variable "tourofbeam_deployment_state_bucket" {
+    description = "Existing GCS bucket's to store Terraform state for Tour of 
Beam deployment"
+}
+
+# Trigger variables
+variable "trigger_source_repo" {
+    default = "https://github.com/beamplayground/deploy-workaround";
+}
+
+variable "trigger_source_branch" {
+  description = "Source branch used for github trigger, not used but reqired 
due to cloudbuild limitation"
+  default = "main"
+}
+
+variable "webhook_trigger_secret_id" {
+  description = "The name of the secret for webhook config cloud build trigger 
(CI/CD)"
+  default = "playground-cicd-webhook"
+}
+
+variable "gh_pat_secret_id" {
+  description = "The name of the secret for GitHub Personal Access Token. 
Required for cloud build trigger (CI/CD)"
+  default = "playground-github-pat-ci"
+}
+
+variable "tourofbeam_deploy_trigger_name" {
+  description = "The name of the trigger to deploy Tour of Beam"
+  default = "TourOfBeam-Deploy"
+}
+
+variable "tourofbeam_ci_trigger_name" {
+  description = "The name of the trigger to run CI checks"
+  default = "TourOfBeam-CI"
+}
+
+variable "tourofbeam_cd_trigger_name" {
+  description = "The name of the trigger to run CD checks"
+    default = "TourOfBeam-CD"
+}
+
+variable "cloudbuild_machine_type" {
+  description = "Machine type used for cloudbuild runtime"
+  default = "E2_HIGHCPU_32"
+}
+
+variable "tourofbeam_cb_private_bucket" {
+  description = "The Google Cloud Platform GCS bucket name for Tour of Beam 
Cloudbuild Private logs"
+}
+
+variable "tourofbeam_cb_public_bucket" {
+  description = "The Google Cloud Platform GCS bucket name for Tour of Beam 
Cloudbuild Public logs"
+}
+
+variable "terraform_source_repo" {
+  description = "Repo used to fetch terraform code"
+  default = "https://github.com/apache/beam";
+}
+
+variable "terraform_source_branch" {
+  description = "Branch used to fetch terraform code"
+  default = "master"
+}
+
+variable "tourofbeam_learning_root" {
+    description = "Existing Tour of Beam learning material root"
+    default = "../learning-content/"
+}
+
+variable "web_app_id" {
+    description = "Tour of Beam web app id"
+    default = "tourofbeam-web-app"
+}
diff --git a/learning/tour-of-beam/cloudbuild/README.md 
b/learning/tour-of-beam/cloudbuild/README.md
new file mode 100644
index 00000000000..cbf09d66493
--- /dev/null
+++ b/learning/tour-of-beam/cloudbuild/README.md
@@ -0,0 +1,153 @@
+<!---
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you 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.
+-->
+
+# Tour of Beam Cloud Build Setup
+
+This directory organizes Infrastructure-as-Code to provision dependent 
resources and set up Cloud Build for automated Tour of Beam Backend 
Infrastructure provisioning, Backend and frontend deployment, manual and 
automated CI/CD.
+
+## Requirements:
+
+1. [GCP 
project](https://cloud.google.com/resource-manager/docs/creating-managing-projects)
+2. Existing Beam Playground environment/infrastructure deployed in same GCP 
Project. Tour of Beam will be deployed to that same location  (region/zone). If 
you don't have one, please follow [Beam Playground deployment 
instructions](./../../../playground/terraform/infrastructure/cloudbuild-manual-setup/README.md)
 to deploy it.
+3. [GCP User 
account](https://cloud.google.com/appengine/docs/standard/access-control?tab=python)
 _(Note: You will find the instruction "How to create User account" for your 
new project)_<br>
+  Ensure that the account has at least the following [IAM 
roles](https://cloud.google.com/iam/docs/understanding-roles):
+
+   - Cloud Datastore Owner
+   - Create Service Accounts
+   - Security Admin
+   - Service Account User
+   - Service Usage Admin
+   - Storage Admin
+   - Kubernetes Engine Cluster Viewer
+   - Secret Manager Admin
+   - Cloud Build Editor
+
+4. [Google Cloud Storage 
buckets](https://cloud.google.com/storage/docs/creating-buckets) for:
+- Terraform state for Cloud Build triggers: \<tourofbeam-triggers-state-env\>
+- Cloud Build private logs: \<tourofbeam-cb-private-logs-env\>
+- Cloud Build public logs: \<tourofbeam-cb-public-logs-env\>. Don't enforce 
public access prevention on this bucket.
+- Terraform state for Tour of Beam deployment: 
\<tourofbeam-deployment-state-env\>
+
+It's advised to add environment name to the bucket name to avoid collisions 
with other environments.
+
+5. An OS with the following software installed:
+
+* [Terraform](https://www.terraform.io/downloads)
+* [gcloud CLI](https://cloud.google.com/sdk/docs/install-sdk)
+* [Kubectl authentication 
plugin](https://cloud.google.com/blog/products/containers-kubernetes/kubectl-auth-changes-in-gke)
+* [Git client](https://git-scm.com/downloads)
+
+DEV NOTE: GCP Cloud shell can be used for deployment. It has all required 
software pre-installed.
+
+6. [Apache Beam GitHub](https://github.com/apache/beam) repository cloned 
locally
+
+# Prepare deployment configuration
+
+1. Generate a Terraform variable file called 
`beam/learning/tour-of-beam/cloudbuild/common.tfvars`. Place the values listed 
below into the file, adjusting them as needed:
+
+project_id               = "apache-beam-testing"      # GCP project ID
+tourofbeam_deploy_sa     = "tourofbeam-cb-deploy-env" # Service account for 
Initialize-Playground-environment trigger
+tourofbeam_ci_sa         = "tourofbeam-cb-ci-env"     # Service account for CI 
trigger
+tourofbeam_cd_sa         = "tourofbeam-cb-cd-env"     # Service account for CD 
trigger
+environment_name         = "env"                      # Environment name
+playground_dns_name      = "fqdn.beam.apache.org"     # Playground DNS name
+pg_region                = "us-west1"                 # Playground region
+pg_gke_zone              = "us-west1-a"               # Playground GKE zone
+pg_gke_name              = "cluster_name"             # Playground GKE cluster 
name
+pg_datastore_namespace   = "env"                      # Playground Datastore 
namespace name
+tourofbeam_deployment_state_bucket  = "tourofbeam-deployment-state-env"     # 
Bucket for Terraform state for deployment
+webhook_trigger_secret_id           = "secret-cloudbuild-triggers-webhook"  # 
Secret ID for webhook trigger
+gh_pat_secret_id                    = "github_pat_playground_deployment"    # 
Secret ID for GitHub PAT
+tourofbeam_deploy_trigger_name      = "TourOfBeam-Deploy-Update-env"        # 
Trigger name for deployment trigger
+tourofbeam_ci_trigger_name   = "TourOfBeam-CI-env"                # Trigger 
name for CI trigger
+tourofbeam_cd_trigger_name   = "TourOfBeam-CD-env"                # Trigger 
name for CD trigger
+tourofbeam_cb_private_bucket = "tourofbeam-cb-private-logs-env"   # Bucket for 
Cloud Build private logs
+tourofbeam_cb_public_bucket  = "tourofbeam-cb-public-logs-env"    # Bucket for 
Cloud Build public logs
+web_app_id = "Tour-Of-Beam"  = "tour-of-beam"                     # Web app ID
+
+If you plan to have only one environment, you can use simple resource names 
like `tourofbeam-deployment-state` or `tourofbeam-cb-private-logs` instead of 
`tourofbeam-deployment-state-env` or `tourofbeam-cb-private-logs-env`. But if 
you plan to have multiple environments, it's advised to add environment name to 
the resource name to avoid collisions with other environments.
+
+## 1. Set up the Google Cloud Build for your GCP project
+
+The `beam/learning/tour-of-beam/cloudbuild/01.setup` provisions dependencies 
required to set up Cloud Build for Tour of Beam:
+- Required API services
+- Cloud Build triggers service accounts
+- IAM roles for Cloud Build service account
+
+#### To execute the module:
+
+1. Run commands:
+
+```console
+
+# Create a new authentication configuration for GCP Project with the created 
user account
+gcloud init
+
+# Command imports new user account credentials into Application Default 
Credentials
+gcloud auth application-default login
+
+# Navigate to 01.setup directory
+cd beam/learning/tour-of-beam/cloudbuild/01.setup
+
+# Run terraform commands
+terraform init -backend-config="bucket=\<tourofbeam-triggers-state-env\>
+terraform apply -var="project_id=$(gcloud config get-value project)" 
-var-file="../common.tfvars"
+```
+
+## 2. Connect default (https://github.com/beamplayground/deploy-workaround) 
GitHub repository with GCP Cloud Build
+
+Follow [Connect to a GitHub 
repository](https://cloud.google.com/build/docs/automating-builds/github/connect-repo-github)
 to connect GitHub repository with GCP Cloud Build.
+
+## 3. Set up the Google Cloud Build triggers
+
+The `beam/learning/tour-of-beam/cloudbuild/02.builders` provisions:
+- Cloud Build triggers to build and deploy Tour of Beam backend infrastructure
+
+#### To execute the module
+
+```
+# Navigate to beam/learning/tour-of-beam/cloudbuild/02.builders directory
+cd ../02.builders
+
+# Run terraform commands and provide required values
+terraform init -backend-config="bucket=\<tourofbeam-triggers-state-env\>"
+
+terraform apply -var="project_id=$(gcloud config get-value project)" 
-var-file="../common.tfvars"
+
+```
+
+## 4. Run Cloud Build trigger to deploy Tour of Beam infrastructure, backend 
and learning materials
+
+1. Navigate to [GCP Console Cloud Build 
Triggers](https://console.cloud.google.com/cloud-build/triggers) page. Choose 
the global region.
+2.  `TourOfBeam-Deploy-Update-env`.
+3. Scroll down to `Source` - `Repository` to ensure that Default GitHub 
repository is connected.
+   - Click on drop-down menu and press `CONNECT NEW REPOSITORY` in case it was 
not automatically connected.
+4. Click `Save` and Run the trigger.
+
+## 5. Run Cloud Build trigger to deploy Tour of Beam infrastructure, backend 
and learning materials
+
+1. Navigate to [GCP Console Cloud Build 
Triggers](https://console.cloud.google.com/cloud-build/triggers) page. Choose 
the global region.
+2. Open Trigger: `TourOfBeam-CD-env-manual`.
+3. Scroll down to `Source` - `Repository` to ensure that Default GitHub 
repository is connected.
+   - Click on drop-down menu and press `CONNECT NEW REPOSITORY` in case it was 
not automatically connected.
+4. Click `Save` and Run the trigger.
+
+## 6. Validate Tour of Beam backend infrastructure deployment
+1. Navigate to Cloud Functions service in GCP.
+2. Check if there are cloud functions with prefix of environment (e.g. prod, 
test) in their names.
+3. Navigate to firebase web app url for newly created environment (e.g. 
https://\<web_app_id\>-\<environment_name\>.web.app).
+4. Check if the web app is accessible and all learning materials and examples 
are working.
\ No newline at end of file
diff --git 
a/learning/tour-of-beam/cloudbuild/scripts/tob_deploy_infra_backend.sh 
b/learning/tour-of-beam/cloudbuild/scripts/tob_deploy_infra_backend.sh
new file mode 100644
index 00000000000..d9714887412
--- /dev/null
+++ b/learning/tour-of-beam/cloudbuild/scripts/tob_deploy_infra_backend.sh
@@ -0,0 +1,89 @@
+#!/usr/bin/env bash
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+
+export DEBIAN_FRONTEND=noninteractive
+
+apt-get -qq  update
+
+apt-get -qq  install -y wget unzip software-properties-common git curl 
apt-transport-https ca-certificates gnupg jq lsb-release
+
+echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] 
https://packages.cloud.google.com/apt cloud-sdk main" | tee -a 
/etc/apt/sources.list.d/google-cloud-sdk.list
+
+curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg |  gpg 
--dearmor -o /usr/share/keyrings/cloud.google.gpg
+
+curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o 
/usr/share/keyrings/docker-archive-keyring.gpg
+
+echo "deb [arch=$(dpkg --print-architecture) 
signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] 
https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" |tee 
/etc/apt/sources.list.d/docker.list
+
+wget -nv 
https://releases.hashicorp.com/terraform/1.4.2/terraform_1.4.2_linux_amd64.zip
+
+unzip terraform_1.4.2_linux_amd64.zip
+
+mv terraform /usr/local/bin/terraform
+
+wget -nv 
https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.10.2-stable.tar.xz
+
+tar xf flutter_linux_3.10.2-stable.tar.xz
+
+git config --global --add safe.directory /usr/local/bin/flutter
+
+mv flutter /usr/local/bin/flutter 
+
+export PATH="$PATH:/usr/local/bin/flutter/bin"
+
+flutter doctor
+
+wget -nv https://firebase.tools/bin/linux/latest
+
+mv latest /usr/local/bin/firebase
+
+chmod +x /usr/local/bin/firebase
+
+export PATH="$PATH:/usr/local/bin/"
+
+firebase --version
+
+apt-get -qq update
+
+apt-get -qq install -y google-cloud-sdk-gke-gcloud-auth-plugin 
google-cloud-sdk openjdk-11-jdk kubectl docker-ce golang
+
+git clone --branch $BRANCH_NAME $REPO_NAME --single-branch
+
+gcloud auth configure-docker $PG_REGION-docker.pkg.dev
+
+gcloud container clusters get-credentials --region $PG_GKE_ZONE $PG_GKE_NAME 
--project $PROJECT_ID
+
+cd beam/learning/tour-of-beam/terraform
+
+gcloud datastore indexes create ../backend/internal/storage/index.yaml
+
+terraform init -backend-config="bucket=${STATE_BUCKET}"
+
+terraform apply -auto-approve -var "pg_router_host=$(kubectl get svc -l 
app=backend-router-grpc -o 
jsonpath='{.items[0].status.loadBalancer.ingress[0].ip}:{.items[0].spec.ports[0].port}')"
+
+cd ../../../
+
+export QUALIFIED_WEB_APP_ID=${WEB_APP_ID}-${TF_VAR_environment}
+
+./gradlew learning:tour-of-beam:terraform:InitFrontend -Pregion=$PG_REGION 
-Pproject_id=$TF_VAR_project_id -Pproject_environment=$TF_VAR_environment 
-Pdns-name=$DNS_NAME -Pwebapp_id=$QUALIFIED_WEB_APP_ID
+
+cd learning/tour-of-beam/backend
+
+go run ./cmd/ci_cd/ci_cd.go
+
diff --git a/learning/tour-of-beam/cloudbuild/scripts/tob_lm_cd.sh 
b/learning/tour-of-beam/cloudbuild/scripts/tob_lm_cd.sh
new file mode 100644
index 00000000000..3cf35788472
--- /dev/null
+++ b/learning/tour-of-beam/cloudbuild/scripts/tob_lm_cd.sh
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+
+export DEBIAN_FRONTEND=noninteractive
+
+apt-get -qq  update
+
+apt-get -qq  install -y wget unzip software-properties-common git curl 
apt-transport-https ca-certificates gnupg jq lsb-release
+
+echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] 
https://packages.cloud.google.com/apt cloud-sdk main" | tee -a 
/etc/apt/sources.list.d/google-cloud-sdk.list
+
+curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg |  gpg 
--dearmor -o /usr/share/keyrings/cloud.google.gpg
+
+curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o 
/usr/share/keyrings/docker-archive-keyring.gpg
+
+echo "deb [arch=$(dpkg --print-architecture) 
signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] 
https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" |tee 
/etc/apt/sources.list.d/docker.list
+
+apt-get -qq update
+
+apt-get -qq install -y google-cloud-sdk golang
+
+git clone --branch master https://github.com/apache/beam.git --single-branch
+
+cd beam/learning/tour-of-beam/backend
+
+go run ./cmd/ci_cd/ci_cd.go
diff --git a/learning/tour-of-beam/terraform/README.md 
b/learning/tour-of-beam/terraform/README.md
index 24b4d83e67e..1df6753c346 100644
--- a/learning/tour-of-beam/terraform/README.md
+++ b/learning/tour-of-beam/terraform/README.md
@@ -32,6 +32,9 @@ Before starting the deployment, ensure that you have the 
following prerequisites
    - Service Usage Admin
    - Storage Admin
    - Kubernetes Engine Cluster Viewer
+   - Firebase Admin
+   - Cloud Functions Admin
+
 
 3. [Google Cloud Storage 
bucket](https://cloud.google.com/storage/docs/creating-buckets) for saving 
deployment state
 
@@ -90,8 +93,7 @@ terraform init -backend-config="bucket=`created_gcs_bucket`"
 7. Run terraform apply to create Tour-Of-Beam backend infrastructure
 
 ```
-terraform plan -var "gcloud_init_account=$(gcloud config get-value 
core/account)" \
--var "environment=prod" \
+terraform plan -var "environment=prod" \
 -var "region=us-west1" \
 -var "project_id=$(gcloud config get-value project)" \
 -var "datastore_namespace=playground-datastore-namespace" \
@@ -99,8 +101,7 @@ terraform plan -var "gcloud_init_account=$(gcloud config 
get-value core/account)
 ```
 
 ```
-terraform apply -var "gcloud_init_account=$(gcloud config get-value 
core/account)" \
--var "environment=prod" \
+terraform apply -var "environment=prod" \
 -var "region=us-west1" \
 -var "project_id=$(gcloud config get-value project)" \
 -var "datastore_namespace=playground-datastore-namespace" \
@@ -133,20 +134,6 @@ const cloudFunctionsBaseUrl = 'https://'
    '$_cloudFunctionsProjectRegion-$_cloudFunctionsProjectId'
    '.cloudfunctions.net/${environment}_';
 
-
-const String kAnalyticsUA = 'UA-73650088-2';
-const String kApiClientURL =
-'https://router.${dns_name}';
-const String kApiJavaClientURL =
-'https://java.${dns_name}';
-const String kApiGoClientURL =
-'https://go.${dns_name}';
-const String kApiPythonClientURL =
-'https://python.${dns_name}';
-const String kApiScioClientURL =
-'https://scio.${dns_name}';
-```
-
 9. Create file .firebaserc under beam/learning/tour-of-beam/frontend
 
    9.1. Navigate to beam/learning/tour-of-beam/frontend.
@@ -232,7 +219,7 @@ You will need to:
 
 1) Remove "locationId" line.
 2) Remove quotes (") from key of "key": "value" pair.
-   3) E.g. `projectId: "cloudbuild-384304"`
+3) E.g. `projectId: "cloudbuild-384304"`
 4) In overall, redacted and ready to be inserted data should be as follows:
 
 ```
diff --git a/learning/tour-of-beam/terraform/build.gradle.kts 
b/learning/tour-of-beam/terraform/build.gradle.kts
index fd24c822906..8c320dd242a 100644
--- a/learning/tour-of-beam/terraform/build.gradle.kts
+++ b/learning/tour-of-beam/terraform/build.gradle.kts
@@ -49,9 +49,8 @@ tasks.register<TerraformTask>("terraformRef") {
 
 tasks.register<TerraformTask>("terraformApplyBackend") {
     group = "backend-deploy"
-    dependsOn("terraformInit")
-
-    val pgRouterHost = if 
(project.extensions.extraProperties.has("pg_router_host")) {
+    
+    val pg_router_host = if 
(project.extensions.extraProperties.has("pg_router_host")) {
         project.extensions.extraProperties["pg_router_host"] as String
     } else {
         "unknown"
@@ -61,29 +60,29 @@ tasks.register<TerraformTask>("terraformApplyBackend") {
         "-auto-approve",
         "-lock=false",
         "-parallelism=3",
-        "-var=pg_router_host=$pgRouterHost",
-        "-var=gcloud_init_account=$(gcloud config get-value core/account)",
+        "-var=pg_router_host=$pg_router_host",
         "-var=project_id=$(gcloud config get-value project)",
         "-var-file=./common.tfvars"
     )
+        
+    tasks.getByName("uploadLearningMaterials").mustRunAfter(this)
 }
 
 tasks.register<TerraformTask>("terraformDestroy") {
     dependsOn("getRouterHost")
 
-    val pgRouterHost = if 
(project.extensions.extraProperties.has("pg_router_host")) {
+    val pg_router_host = if 
(project.extensions.extraProperties.has("pg_router_host")) {
         project.extensions.extraProperties["pg_router_host"] as String
     } else {
         "unknown"
     }
     args(
-        "destroy",
-        "-auto-approve",
-        "-lock=false",
-        "-var=pg_router_host=$pgRouterHost",
-        "-var=gcloud_init_account=$(gcloud config get-value core/account)",
-        "-var=project_id=$(gcloud config get-value project)",
-        "-var-file=./common.tfvars"
+            "destroy",
+            "-auto-approve",
+            "-lock=false",
+            "-var=pg_router_host=$pg_router_host",
+            "-var=project_id=$(gcloud config get-value project)",
+            "-var-file=./common.tfvars"
     )
 }
 
@@ -147,6 +146,54 @@ tasks.register("firebaseProjectCreate") {
     }
 }
 
+tasks.register("firebaseHostingCreate") {
+    group = "frontend-deploy"
+    dependsOn("firebaseWebAppCreate")
+
+    doLast {
+        val projectId = project.property("project_id") as String
+        val webapp_id = project.property("webapp_id") as String
+        val listSitesResult = ByteArrayOutputStream()
+        exec {
+            executable("firebase")
+            args("hosting:sites:list", "--project", projectId)
+            workingDir("../frontend")
+            standardOutput = listSitesResult
+        }
+
+        println(listSitesResult)
+        val output = listSitesResult.toString()
+        val regex = "\\b$webapp_id\\b".toRegex()
+        if (regex.containsMatchIn(output)) {
+            println("Firebase is already added to project $projectId.")
+        } else {
+            exec {
+                executable("firebase")
+                args("hosting:sites:create", webapp_id)
+                workingDir("../frontend")
+            }.assertNormalExitValue()
+            println("Firebase hosting site has been added to project 
$projectId.")
+        }
+    
+        exec {
+            executable("firebase")
+            args("target:apply", "hosting", webapp_id , webapp_id)
+            workingDir("../frontend")
+
+        }.assertNormalExitValue()
+
+        val file = project.file("../frontend/firebase.json")
+        val content = file.readText()
+        
+        val oldContent = """"public": "build/web","""
+        val newContent = """"public": "build/web",
+        "target": "$webapp_id","""
+        val updatedContent = content.replace(oldContent, newContent)
+        
+        file.writeText(updatedContent)
+    }
+}
+
 tasks.register("firebaseWebAppCreate") {
     group = "frontend-deploy"
     dependsOn("firebaseProjectCreate")
@@ -164,8 +211,9 @@ tasks.register("firebaseWebAppCreate") {
         val output = result.toString()
         if (output.contains(webappId)) {
             println("Webapp id $webappId is already created on the project: 
$projectId.")
-            val regex = Regex("$webappId[│ ]+([\\w:]+)[│ ]+WEB[│ ]+")
+            val regex = Regex("""$webappId[\W]+([\d:a-zA-Z]+)[\W]+WEB""")
             val firebaseAppId = regex.find(output)?.groupValues?.get(1)?.trim()
+            println("Firebase app ID for existing Firebase Web App: 
$firebaseAppId")
             project.extensions.extraProperties["firebaseAppId"] = firebaseAppId
         } else {
             val result2 = ByteArrayOutputStream()
@@ -185,7 +233,7 @@ tasks.register("firebaseWebAppCreate") {
 // firebase apps:sdkconfig WEB AppId
 tasks.register("getSdkConfigWebApp") {
     group = "frontend-deploy"
-    dependsOn("firebaseWebAppCreate")
+    dependsOn("firebaseHostingCreate")
 
     doLast {
         val firebaseAppId = 
project.extensions.extraProperties["firebaseAppId"] as String
@@ -227,6 +275,7 @@ tasks.register("prepareFirebaseOptionsDart") {
 }
 
 tasks.register("flutterPubGetPG") {
+    dependsOn("prepareFirebaseOptionsDart")
     doLast {
         exec {
             executable("flutter")
@@ -290,8 +339,9 @@ tasks.register("firebaseDeploy") {
 
     doLast {
         val projectId = project.property("project_id") as String
+        val webapp_id = project.property("webapp_id") as String
         exec {
-            commandLine("firebase", "deploy", "--project", projectId)
+            commandLine("firebase", "deploy", "--only",  "hosting:$webapp_id", 
"--project", projectId)
             workingDir("../frontend")
         }
     }
@@ -317,18 +367,6 @@ const cloudFunctionsBaseUrl = 'https://'
     '$region-$projectId'
     '.cloudfunctions.net/${environment}_';
 
-
-const String kAnalyticsUA = 'UA-73650088-2';
-const String kApiClientURL =
-'https://router.${dnsName}';
-const String kApiJavaClientURL =
-'https://java.${dnsName}';
-const String kApiGoClientURL =
-'https://go.${dnsName}';
-const String kApiPythonClientURL =
-'https://python.${dnsName}';
-const String kApiScioClientURL =
-'https://scio.${dnsName}';
 """
         )
     }
@@ -397,6 +435,7 @@ tasks.register("InitFrontend") {
     dependsOn("prepareFirebasercConfig")
     dependsOn("firebaseProjectCreate")
     dependsOn("firebaseWebAppCreate")
+    dependsOn("firebaseHostingCreate")
     dependsOn("getSdkConfigWebApp")
     dependsOn("prepareFirebaseOptionsDart")
     dependsOn("flutterPubGetPG")
diff --git a/learning/tour-of-beam/terraform/cloud_functions/main.tf 
b/learning/tour-of-beam/terraform/cloud_functions/main.tf
index e80225115db..e519c9378aa 100644
--- a/learning/tour-of-beam/terraform/cloud_functions/main.tf
+++ b/learning/tour-of-beam/terraform/cloud_functions/main.tf
@@ -22,7 +22,7 @@ resource "google_cloudfunctions_function" "cloud_function" {
   runtime               = "go116"
   available_memory_mb   = 128
   project               = var.project_id
-  service_account_email = var.service_account_id
+  service_account_email = var.cf-service-account-id
   source_archive_bucket = var.source_archive_bucket
   source_archive_object = var.source_archive_object
   region                = var.region
diff --git a/learning/tour-of-beam/terraform/cloud_functions/variables.tf 
b/learning/tour-of-beam/terraform/cloud_functions/variables.tf
index c7442359de6..258c52c9411 100644
--- a/learning/tour-of-beam/terraform/cloud_functions/variables.tf
+++ b/learning/tour-of-beam/terraform/cloud_functions/variables.tf
@@ -16,7 +16,7 @@
 # under the License.
 
 # Taken from output of SETUP module
-variable "service_account_id" {
+variable "cf-service-account-id" {
   description = "The name of Service Account to run Cloud Function"
 }
 
diff --git a/learning/tour-of-beam/terraform/functions_buckets/locals.tf 
b/learning/tour-of-beam/terraform/functions_buckets/locals.tf
index ec2f63cb222..e909e43dd7b 100644
--- a/learning/tour-of-beam/terraform/functions_buckets/locals.tf
+++ b/learning/tour-of-beam/terraform/functions_buckets/locals.tf
@@ -15,21 +15,14 @@
 # specific language governing permissions and limitations
 # under the License.
 
-# Random string generator resource. To generate source code buckets name
-resource "random_string" "id" {
-  length = 4
-  upper = false
-  special = false
-}
-
 # Variable for prefix. Used in generated source code buckets name
 variable "resource_name_prefix" {
   type = string
   description = "The resource name prefix applied to all resource naming for 
the application"
-  default = "tour-of-beam"
+  default = "tourofbeam"
 }
 
 # Local value to store generated GCS bucket name for source code (Cloud 
Functions)
 locals {
-  cloudfunctions_bucket = 
"${var.resource_name_prefix}-cfstorage-${random_string.id.result}"
+  cloudfunctions_bucket = 
"${var.resource_name_prefix}-cfstorage-${var.environment}"
 }
\ No newline at end of file
diff --git a/learning/tour-of-beam/terraform/functions_buckets/variables.tf 
b/learning/tour-of-beam/terraform/functions_buckets/variables.tf
index f1cb931f2a2..b8ab4cf99cc 100644
--- a/learning/tour-of-beam/terraform/functions_buckets/variables.tf
+++ b/learning/tour-of-beam/terraform/functions_buckets/variables.tf
@@ -19,3 +19,7 @@
 variable "region" {
   description = "The GCP region where GCS bucket will be created (For Cloud 
Functions source code)"
 }
+
+variable "environment" {
+  description = "The name of the environment for deployment. Will create 
directory where terraform config files will be stored"
+}
diff --git a/learning/tour-of-beam/terraform/main.tf 
b/learning/tour-of-beam/terraform/main.tf
index ba122fc7acb..f37a4435a65 100644
--- a/learning/tour-of-beam/terraform/main.tf
+++ b/learning/tour-of-beam/terraform/main.tf
@@ -19,14 +19,15 @@
 module "setup" {
   source = "./setup"
   project_id = var.project_id
-  gcloud_init_account = var.gcloud_init_account
   depends_on = [module.api_enable]
+  environment = var.environment
 }
 
 # GCS buckets to create buckets, objects, archive to store source code that 
cloud functions will use
 module "functions_buckets" {
   source = "./functions_buckets"
   region      = var.region
+  environment = var.environment
   depends_on = [module.setup, module.api_enable]
 }
 
@@ -43,7 +44,7 @@ module "cloud_functions" {
   pg_router_host = var.pg_router_host
   environment = var.environment
   datastore_namespace = var.datastore_namespace
-  service_account_id = module.setup.service-account-email
+  cf-service-account-id = module.setup.cf-service-account-id
   source_archive_bucket = module.functions_buckets.functions-bucket-name
   source_archive_object = module.functions_buckets.function-bucket-object
   depends_on = [module.functions_buckets, module.setup, module.api_enable]
diff --git a/learning/tour-of-beam/terraform/setup/iam.tf 
b/learning/tour-of-beam/terraform/setup/iam.tf
index 245872364fd..0b0eaf28abb 100644
--- a/learning/tour-of-beam/terraform/setup/iam.tf
+++ b/learning/tour-of-beam/terraform/setup/iam.tf
@@ -18,7 +18,7 @@
 # Service account for GCP Cloud Functions
 resource "google_service_account" "cloud_function_sa" {
   account_id   = local.cloudfunctions_service_account
-  display_name = "Service Account to run Cloud Functions"
+  display_name = "Tour of Beam CF Service Account-${var.environment}"
 }
 
 # IAM roles for Cloud Functions service account
@@ -32,13 +32,3 @@ resource "google_project_iam_member" 
"terraform_service_account_roles" {
   member  = "serviceAccount:${google_service_account.cloud_function_sa.email}"
   project = var.project_id
 }
-
-# IAM roles to be granted for user account that will be running terraform 
scripts
-resource "google_project_iam_member" "gcloud_user_required_roles" {
-  for_each = toset([
-    "roles/cloudfunctions.admin", "roles/firebase.admin"
-  ])
-  role    = each.key
-  member  = "user:${var.gcloud_init_account}"
-  project = var.project_id
-}
diff --git a/learning/tour-of-beam/terraform/setup/locals.tf 
b/learning/tour-of-beam/terraform/setup/locals.tf
index 9817b8f2821..6ebf0e359e6 100644
--- a/learning/tour-of-beam/terraform/setup/locals.tf
+++ b/learning/tour-of-beam/terraform/setup/locals.tf
@@ -17,18 +17,13 @@
 
 # Local value to store generated Cloud Functions' Service account name
 
-resource "random_string" "id" {
-  length = 4
-  upper = false
-  special = false
-}
 
 variable "resource_name_prefix" {
   type = string
   description = "The resource name prefix applied to all resource naming for 
the application"
-  default = "tour-of-beam"
+  default = "tourofbeam"
 }
 
 locals {
-  cloudfunctions_service_account = 
"${var.resource_name_prefix}-cf-sa-${random_string.id.result}"
+  cloudfunctions_service_account = 
"${var.resource_name_prefix}-cf-sa-${var.environment}"
 }
\ No newline at end of file
diff --git a/learning/tour-of-beam/terraform/setup/output.tf 
b/learning/tour-of-beam/terraform/setup/output.tf
index bd069f1ed8d..4ae6894e130 100644
--- a/learning/tour-of-beam/terraform/setup/output.tf
+++ b/learning/tour-of-beam/terraform/setup/output.tf
@@ -16,6 +16,6 @@
 # under the License.
 
 # Output used to assign service account to cloud functions
-output "service-account-email" {
+output "cf-service-account-id" {
   value = google_service_account.cloud_function_sa.email
 }
\ No newline at end of file
diff --git a/learning/tour-of-beam/terraform/setup/variables.tf 
b/learning/tour-of-beam/terraform/setup/variables.tf
index dcb6f54808b..f5c231a2851 100644
--- a/learning/tour-of-beam/terraform/setup/variables.tf
+++ b/learning/tour-of-beam/terraform/setup/variables.tf
@@ -21,7 +21,7 @@ variable "project_id" {
   description = "The ID of the Google Cloud project within which resources are 
provisioned"
 }
 
-# This variable is generated by command (gcloud config get-value core/account) 
in Kotlin Gradle script
-variable "gcloud_init_account" {
-  description = "User Account ID logged in with gcloud init command (e.g. 
[email protected])"
+variable "environment" {
+  description = "The name of the environment for deployment. Will create 
directory where terraform config files will be stored"
 }
+
diff --git a/learning/tour-of-beam/terraform/variables.tf 
b/learning/tour-of-beam/terraform/variables.tf
index 0a045514dd5..760d6614587 100644
--- a/learning/tour-of-beam/terraform/variables.tf
+++ b/learning/tour-of-beam/terraform/variables.tf
@@ -16,7 +16,6 @@
 # under the License.
 
 # GCP Project ID
-# Provided as a result of gcloud command
 variable "project_id" {
   description = "The ID of the Google Cloud project within which resources are 
provisioned"
 }
@@ -26,12 +25,6 @@ variable "region" {
   description = "The region of the Google Cloud project within which resources 
are provisioned"
 }
 
-# User account that will be deploying Tour of Beam infrastructure
-# Provided as a result of gcloud command
-variable "gcloud_init_account" {
-  description = "User Account ID logged in with gcloud init command (e.g. 
[email protected])"
-}
-
 # Existing Playground router hostname:port details
 # Provided as a result of kubectl command
 variable "pg_router_host" {
@@ -46,4 +39,4 @@ variable "environment" {
 
 variable "datastore_namespace" {
   description = "The name of datastore namespace"
-}
\ No newline at end of file
+}
diff --git 
a/playground/frontend/playground_components/lib/src/constants/backend_urls.dart 
b/playground/frontend/playground_components/lib/src/constants/backend_urls.dart
index 670dfea2dad..20c2d6348d9 100644
--- 
a/playground/frontend/playground_components/lib/src/constants/backend_urls.dart
+++ 
b/playground/frontend/playground_components/lib/src/constants/backend_urls.dart
@@ -19,7 +19,7 @@
 // ignore_for_file: prefer_interpolation_to_compose_strings
 
 /// The template for production backend URL.
-const defaultBackendUrlTemplate = 'https://{node}.play-dev.beam.apache.org';
+const defaultBackendUrlTemplate = 'https://{node}.play.beam.apache.org';
 
 /// The URLs for local backend development.
 const backendUrlOverrides = <String, String>{

Reply via email to