diff --git a/platforms/gke/base/_shared_config/scripts/set_environment_variables.sh b/platforms/gke/base/_shared_config/scripts/set_environment_variables.sh
index e8aaf0b4..71d3778f 100755
--- a/platforms/gke/base/_shared_config/scripts/set_environment_variables.sh
+++ b/platforms/gke/base/_shared_config/scripts/set_environment_variables.sh
@@ -13,15 +13,15 @@
 # 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.
-SHARED_CONFIG_PATHS=${@}
+SHARED_CONFIG_PATHS=("${@}")
 
-for SHARED_CONFIG_PATH in ${SHARED_CONFIG_PATHS}; do
-    echo "Loading shared configuration(${SHARED_CONFIG_PATH})"
-    echo "-------------------------------------------------------------------------"
-    cd ${SHARED_CONFIG_PATH} || exit 1
-    terraform init >/dev/null
-    terraform apply -auto-approve -input=false >/dev/null
-    terraform output
-    echo -e "-------------------------------------------------------------------------\n"
-    eval $(terraform output | sed -r 's/(\".*\")|\s*/\1/g')
+for SHARED_CONFIG_PATH in "${SHARED_CONFIG_PATHS[@]}"; do
+  echo "Loading shared configuration(${SHARED_CONFIG_PATH})"
+  echo "-------------------------------------------------------------------------"
+  cd "${SHARED_CONFIG_PATH}" || exit 1
+  terraform init >/dev/null
+  terraform apply -auto-approve -input=false >/dev/null
+  terraform output
+  echo -e "-------------------------------------------------------------------------\n"
+  eval "$(terraform output | sed -r 's/(\".*\")|\s*/\1/g')"
 done
diff --git a/platforms/gke/base/core/deploy.sh b/platforms/gke/base/core/deploy.sh
index 58ec5326..b9e0ba96 100755
--- a/platforms/gke/base/core/deploy.sh
+++ b/platforms/gke/base/core/deploy.sh
@@ -14,57 +14,66 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 set -o errexit
+set -o nounset
+set -o pipefail
+
+# shellcheck disable=SC1091
+source "${ACP_PLATFORM_CORE_DIR}/functions.sh"
 
 start_timestamp=$(date +%s)
 
 declare -a terraservices
-if [[ -v CORE_TERRASERVICES_APPLY ]]; then
-    terraservices=("${CORE_TERRASERVICES_APPLY[@]}")
+if [[ -v CORE_TERRASERVICES_APPLY ]] &&
+  [[ -n "${CORE_TERRASERVICES_APPLY:-""}" ]]; then
+  echo "Found customized core platform terraservices set to apply: ${CORE_TERRASERVICES_APPLY}"
+  ParseSpaceSeparatedBashArray "${CORE_TERRASERVICES_APPLY}" "terraservices"
 else
-    terraservices=(
-        "networking"
-        "container_cluster"
-        "container_node_pool"
-        "gke_enterprise/fleet_membership"
-        # Disable gke_enterprise/servicemesh due to b/376312292
-        # "gke_enterprise/servicemesh"
-        "workloads/kueue"
-    )
+  terraservices=(
+    "networking"
+    "container_cluster"
+    "container_node_pool"
+    "gke_enterprise/fleet_membership"
+    # Disable gke_enterprise/servicemesh due to b/376312292
+    # "gke_enterprise/servicemesh"
+    "workloads/kueue"
+  )
 fi
 echo "Core platform terraservices to provision: ${terraservices[*]}"
 
-source ${ACP_PLATFORM_BASE_DIR}/_shared_config/scripts/set_environment_variables.sh ${ACP_PLATFORM_BASE_DIR}/_shared_config
+# shellcheck disable=SC1091
+source "${ACP_PLATFORM_BASE_DIR}/_shared_config/scripts/set_environment_variables.sh" "${ACP_PLATFORM_BASE_DIR}/_shared_config"
 
-cd ${ACP_PLATFORM_CORE_DIR}/initialize &&
-    echo "Current directory: $(pwd)" &&
-    sed -i "s/^\([[:blank:]]*bucket[[:blank:]]*=\).*$/\1 \"${terraform_bucket_name}\"/" ${ACP_PLATFORM_CORE_DIR}/initialize/backend.tf.bucket &&
-    export STATE_MIGRATED="false" &&
-    if gcloud storage ls gs://${terraform_bucket_name}/terraform/initialize/default.tfstate &>/dev/null; then
-        if [ ! -f ${ACP_PLATFORM_CORE_DIR}/initialize/backend.tf ]; then
-            cp backend.tf.bucket backend.tf
-        fi
-        export STATE_MIGRATED="true"
+# shellcheck disable=SC2154 # Variable is defined as a terraform output and sourced in other scripts
+cd "${ACP_PLATFORM_CORE_DIR}/initialize" &&
+  echo "Current directory: $(pwd)" &&
+  sed -i "s/^\([[:blank:]]*bucket[[:blank:]]*=\).*$/\1 \"${terraform_bucket_name}\"/" "${ACP_PLATFORM_CORE_DIR}/initialize/backend.tf.bucket" &&
+  export STATE_MIGRATED="false" &&
+  if gcloud storage ls "gs://${terraform_bucket_name}/terraform/initialize/default.tfstate" &>/dev/null; then
+    if [ ! -f "${ACP_PLATFORM_CORE_DIR}/initialize/backend.tf" ]; then
+      cp backend.tf.bucket backend.tf
     fi
+    export STATE_MIGRATED="true"
+  fi
 
-cd ${ACP_PLATFORM_CORE_DIR}/initialize &&
-    terraform init &&
-    terraform plan -input=false -out=tfplan &&
-    terraform apply -input=false tfplan || exit 1
+cd "${ACP_PLATFORM_CORE_DIR}/initialize" &&
+  terraform init &&
+  terraform plan -input=false -out=tfplan &&
+  terraform apply -input=false tfplan || exit 1
 rm tfplan
 
-if [ ${STATE_MIGRATED} == "false" ]; then
-    echo "Migrating the state backend"
-    terraform init -force-copy -migrate-state || exit 1
-    rm -rf terraform.tfstate*
+if [ "${STATE_MIGRATED}" == "false" ]; then
+  echo "Migrating the state backend"
+  terraform init -force-copy -migrate-state || exit 1
+  rm -rf terraform.tfstate*
 fi
 
 for terraservice in "${terraservices[@]}"; do
-    cd "${ACP_PLATFORM_CORE_DIR}/${terraservice}" &&
-        echo "Current directory: $(pwd)" &&
-        terraform init &&
-        terraform plan -input=false -out=tfplan &&
-        terraform apply -input=false tfplan || exit 1
-    rm tfplan
+  cd "${ACP_PLATFORM_CORE_DIR}/${terraservice}" &&
+    echo "Current directory: $(pwd)" &&
+    terraform init &&
+    terraform plan -input=false -out=tfplan &&
+    terraform apply -input=false tfplan || exit 1
+  rm tfplan
 done
 
 end_timestamp=$(date +%s)
diff --git a/platforms/gke/base/core/functions.sh b/platforms/gke/base/core/functions.sh
new file mode 100755
index 00000000..c9380c9a
--- /dev/null
+++ b/platforms/gke/base/core/functions.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+#
+# 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.
+
+ParseSeparatedBashArray() {
+  local STRING_TO_PARSE="${1}"
+  local -n DESTINATION_ARRAY="${2}"
+  local STRING_ARRAY_SEPARATOR="${3}"
+
+  echo "Parsing ${STRING_TO_PARSE} as a Bash array"
+
+  local -a PARSED_ARRAY
+  IFS="${STRING_ARRAY_SEPARATOR}" read -r -a PARSED_ARRAY <<<"${STRING_TO_PARSE}"
+  echo "Elements to add to ${!DESTINATION_ARRAY}: ${PARSED_ARRAY[*]}"
+
+  DESTINATION_ARRAY+=("${PARSED_ARRAY[@]}")
+  echo "${!DESTINATION_ARRAY} after adding options: ${DESTINATION_ARRAY[*]}"
+
+  unset -n DESTINATION_ARRAY
+}
+
+ParseSpaceSeparatedBashArray() {
+  ParseSeparatedBashArray "${1}" "${2}" " "
+}
diff --git a/platforms/gke/base/core/teardown.sh b/platforms/gke/base/core/teardown.sh
index 0843e2fd..e1ba452f 100755
--- a/platforms/gke/base/core/teardown.sh
+++ b/platforms/gke/base/core/teardown.sh
@@ -14,71 +14,82 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 set -o errexit
+set -o nounset
+set -o pipefail
+
+# shellcheck disable=SC1091
+source "${ACP_PLATFORM_CORE_DIR}/functions.sh"
 
 start_timestamp=$(date +%s)
 
 declare -a terraservices
-if [[ -v CORE_TERRASERVICES_DESTROY ]]; then
-    terraservices=("${CORE_TERRASERVICES_DESTROY[@]}")
+if [[ -v CORE_TERRASERVICES_DESTROY ]] &&
+  [[ -n "${CORE_TERRASERVICES_DESTROY:-""}" ]]; then
+  echo "Found customized core platform terraservices set to destroy: ${CORE_TERRASERVICES_DESTROY}"
+  ParseSpaceSeparatedBashArray "${CORE_TERRASERVICES_DESTROY}" "terraservices"
 else
-    terraservices=(
-        "workloads/kueue"
-        # Disable gke_enterprise/servicemesh due to b/376312292
-        # "gke_enterprise/servicemesh"
-        "gke_enterprise/fleet_membership"
-        "container_node_pool"
-        "container_cluster"
-        "networking"
-    )
+  terraservices=(
+    "workloads/kueue"
+    # Disable gke_enterprise/servicemesh due to b/376312292
+    # "gke_enterprise/servicemesh"
+    "gke_enterprise/fleet_membership"
+    "container_node_pool"
+    "container_cluster"
+    "networking"
+  )
 fi
 echo "Core platform terraservices to destroy: ${terraservices[*]}"
 
-source ${ACP_PLATFORM_BASE_DIR}/_shared_config/scripts/set_environment_variables.sh ${ACP_PLATFORM_BASE_DIR}/_shared_config
+# shellcheck disable=SC1091
+source "${ACP_PLATFORM_BASE_DIR}/_shared_config/scripts/set_environment_variables.sh" "${ACP_PLATFORM_BASE_DIR}/_shared_config"
 
-cd ${ACP_PLATFORM_CORE_DIR}/initialize &&
-    echo "Current directory: $(pwd)" &&
-    sed -i "s/^\([[:blank:]]*bucket[[:blank:]]*=\).*$/\1 \"${terraform_bucket_name}\"/" ${ACP_PLATFORM_CORE_DIR}/initialize/backend.tf.bucket &&
-    cp backend.tf.bucket backend.tf &&
-    terraform init &&
-    terraform plan -input=false -out=tfplan &&
-    terraform apply -input=false tfplan || exit 1
+# shellcheck disable=SC2154 # Variable is defined as a terraform output and sourced in other scripts
+cd "${ACP_PLATFORM_CORE_DIR}/initialize" &&
+  echo "Current directory: $(pwd)" &&
+  sed -i "s/^\([[:blank:]]*bucket[[:blank:]]*=\).*$/\1 \"${terraform_bucket_name}\"/" "${ACP_PLATFORM_CORE_DIR}/initialize/backend.tf.bucket" &&
+  cp backend.tf.bucket backend.tf &&
+  terraform init &&
+  terraform plan -input=false -out=tfplan &&
+  terraform apply -input=false tfplan || exit 1
 rm tfplan
 
 for terraservice in "${terraservices[@]}"; do
-    cd "${ACP_PLATFORM_CORE_DIR}/${terraservice}" &&
-        echo "Current directory: $(pwd)" &&
-        terraform init &&
-        terraform destroy -auto-approve || exit 1
-    rm -rf .terraform/
-done
-
-cd ${ACP_PLATFORM_CORE_DIR}/initialize &&
+  cd "${ACP_PLATFORM_CORE_DIR}/${terraservice}" &&
     echo "Current directory: $(pwd)" &&
-    rm -rf backend.tf &&
-    terraform init -force-copy -lock=false -migrate-state || exit 1
-gcloud storage rm -r gs://${terraform_bucket_name}/* &&
+    terraform init &&
     terraform destroy -auto-approve || exit 1
+  rm -rf .terraform/
+done
+
+cd "${ACP_PLATFORM_CORE_DIR}/initialize" &&
+  echo "Current directory: $(pwd)" &&
+  rm -rf backend.tf &&
+  terraform init -force-copy -lock=false -migrate-state || exit 1
+# Quote the globbing expression because we don't want to expand it with the
+# shell
+gcloud storage rm -r "gs://${terraform_bucket_name}/*" &&
+  terraform destroy -auto-approve || exit 1
 
 rm -rf \
-    ${ACP_PLATFORM_BASE_DIR}/_shared_config/.terraform/ \
-    ${ACP_PLATFORM_BASE_DIR}/_shared_config/terraform.tfstate* \
-    ${ACP_PLATFORM_CORE_DIR}/initialize/.terraform/ \
-    ${ACP_PLATFORM_CORE_DIR}/initialize/terraform.tfstate* \
-    ${ACP_PLATFORM_CORE_DIR}/networking/.terraform/ \
-    ${ACP_PLATFORM_CORE_DIR}/container_cluster/.terraform/ \
-    ${ACP_PLATFORM_CORE_DIR}/container_node_pool/.terraform/ \
-    ${ACP_PLATFORM_CORE_DIR}/container_node_pool/container_node_pool_*.tf \
-    ${ACP_PLATFORM_CORE_DIR}/gke_enterprise/configmanagement/git/.terraform/ \
-    ${ACP_PLATFORM_CORE_DIR}/gke_enterprise/configmanagement/oci/.terraform/ \
-    ${ACP_PLATFORM_CORE_DIR}/gke_enterprise/fleet_membership/.terraform/ \
-    ${ACP_PLATFORM_CORE_DIR}/gke_enterprise/servicemesh/.terraform/ \
-    ${ACP_PLATFORM_CORE_DIR}/workloads/kueue.terraform/ \
-    ${ACP_PLATFORM_CORE_DIR}/workloads/kubeconfig \
-    ${ACP_PLATFORM_CORE_DIR}/workloads/manifests
+  "${ACP_PLATFORM_BASE_DIR}/_shared_config/.terraform/" \
+  "${ACP_PLATFORM_BASE_DIR}/_shared_config"/terraform.tfstate* \
+  "${ACP_PLATFORM_CORE_DIR}/initialize/.terraform/" \
+  "${ACP_PLATFORM_CORE_DIR}/initialize"/terraform.tfstate* \
+  "${ACP_PLATFORM_CORE_DIR}/networking/.terraform/" \
+  "${ACP_PLATFORM_CORE_DIR}/container_cluster/.terraform/" \
+  "${ACP_PLATFORM_CORE_DIR}/container_node_pool/.terraform/" \
+  "${ACP_PLATFORM_CORE_DIR}/container_node_pool"/container_node_pool_*.tf \
+  "${ACP_PLATFORM_CORE_DIR}/gke_enterprise/configmanagement/git/.terraform/" \
+  "${ACP_PLATFORM_CORE_DIR}/gke_enterprise/configmanagement/oci/.terraform/" \
+  "${ACP_PLATFORM_CORE_DIR}/gke_enterprise/fleet_membership/.terraform/" \
+  "${ACP_PLATFORM_CORE_DIR}/gke_enterprise/servicemesh/.terraform/" \
+  "${ACP_PLATFORM_CORE_DIR}/workloads/kueue.terraform/" \
+  "${ACP_PLATFORM_CORE_DIR}/workloads/kubeconfig" \
+  "${ACP_PLATFORM_CORE_DIR}/workloads/manifests"
 
 git restore \
-    ${ACP_PLATFORM_CORE_DIR}/initialize/backend.tf.bucket \
-    ${ACP_PLATFORM_CORE_DIR}/container_node_pool/container_node_pool_*.tf
+  "${ACP_PLATFORM_CORE_DIR}/initialize/backend.tf.bucket" \
+  "${ACP_PLATFORM_CORE_DIR}/container_node_pool"/container_node_pool_*.tf
 
 end_timestamp=$(date +%s)
 total_runtime_value=$((end_timestamp - start_timestamp))
diff --git a/platforms/gke/base/use-cases/federated-learning/common.sh b/platforms/gke/base/use-cases/federated-learning/common.sh
index 02752f86..edfd7723 100755
--- a/platforms/gke/base/use-cases/federated-learning/common.sh
+++ b/platforms/gke/base/use-cases/federated-learning/common.sh
@@ -37,6 +37,7 @@ FEDERATED_LEARNING_SHARED_CONFIG_DIR="${FEDERATED_LEARNING_USE_CASE_TERRAFORM_DI
 # Terraservices that are necessary for the core platform
 federated_learning_core_platform_terraservices=(
   "key_management_service"
+  "service_account"
 )
 
 # shellcheck disable=SC2034 # Variable is used in other scripts
diff --git a/platforms/gke/base/use-cases/federated-learning/deploy.sh b/platforms/gke/base/use-cases/federated-learning/deploy.sh
index c6484394..cbce99f5 100755
--- a/platforms/gke/base/use-cases/federated-learning/deploy.sh
+++ b/platforms/gke/base/use-cases/federated-learning/deploy.sh
@@ -26,10 +26,9 @@ start_timestamp_federated_learning=$(date +%s)
 echo "Initializing the core platform"
 # Don't provision any core platform terraservice becuase we just need
 # to initialize the terraform environment and remote backend
-declare -a CORE_TERRASERVICES_APPLY
-CORE_TERRASERVICES_APPLY=("initialize")
 # shellcheck disable=SC1091
-source "${ACP_PLATFORM_CORE_DIR}/deploy.sh"
+CORE_TERRASERVICES_APPLY="initialize" \
+  "${ACP_PLATFORM_CORE_DIR}/deploy.sh"
 
 echo "Preparing core platform configuration files"
 for configuration_variable in "${TERRAFORM_CLUSTER_CONFIGURATION[@]}"; do
@@ -51,10 +50,9 @@ fi
 edit_terraform_configuration_variable_value_in_file "cluster_database_encryption_key_name_placeholder" "${cluster_database_encryption_key_id}" "${ACP_PLATFORM_SHARED_CONFIG_CLUSTER_AUTO_VARS_FILE}"
 
 echo "Provisioning the core platform"
-# shellcheck disable=SC2034 # Variable is used in other scripts
-CORE_TERRASERVICES_APPLY=("networking" "container_cluster" "gke_enterprise/fleet_membership")
-# shellcheck disable=SC1091
-source "${ACP_PLATFORM_CORE_DIR}/deploy.sh"
+# shellcheck disable=SC1091,SC2034 # Variable is used in other scripts
+CORE_TERRASERVICES_APPLY="networking container_cluster gke_enterprise/fleet_membership" \
+  "${ACP_PLATFORM_CORE_DIR}/deploy.sh"
 
 echo "Provisioning the use case resources"
 # shellcheck disable=SC2154 # variable defined in common.sh
diff --git a/platforms/gke/base/use-cases/federated-learning/terraform/_shared_config/uc_federated_learning_variables.tf b/platforms/gke/base/use-cases/federated-learning/terraform/_shared_config/uc_federated_learning_variables.tf
index baa23d07..6bb66e4c 100644
--- a/platforms/gke/base/use-cases/federated-learning/terraform/_shared_config/uc_federated_learning_variables.tf
+++ b/platforms/gke/base/use-cases/federated-learning/terraform/_shared_config/uc_federated_learning_variables.tf
@@ -15,4 +15,29 @@
 locals {
   gke_robot_service_account           = "service-${data.google_project.default.number}@container-engine-robot.iam.gserviceaccount.com"
   gke_robot_service_account_iam_email = "serviceAccount:${local.gke_robot_service_account}"
+
+  tenants = {
+    for name in var.federated_learning_tenant_names : name => {
+      tenant_name                                 = name
+      tenant_nodepool_name                        = format("%s-%s-p", local.cluster_name, name)
+      tenant_nodepool_sa_name                     = format("%s-%s-n", local.cluster_name, name)
+      tenant_apps_sa_name                         = format("%s-%s-a", local.cluster_name, name)
+      tenant_apps_kubernetes_service_account_name = local.tenant_apps_kubernetes_service_account_name
+    }
+  }
+
+  # Put all service account names in a list so we can create them with a single
+  # google_service_account resource
+  service_account_names = concat(
+    [for tenant in local.tenants : tenant.tenant_nodepool_sa_name],
+    [for tenant in local.tenants : tenant.tenant_apps_sa_name],
+  )
+
+  tenant_apps_kubernetes_service_account_name = "fl-ksa"
+}
+
+variable "federated_learning_tenant_names" {
+  default     = ["fl-1"]
+  description = "List of named tenants to be created in the cluster. Each tenant gets a dedicated node pool and Kubernetes namespace, isolated from other tenants."
+  type        = list(string)
 }
diff --git a/platforms/gke/base/use-cases/federated-learning/terraform/container_image_repository/main.tf b/platforms/gke/base/use-cases/federated-learning/terraform/container_image_repository/main.tf
index 80282b51..ade3d6b7 100644
--- a/platforms/gke/base/use-cases/federated-learning/terraform/container_image_repository/main.tf
+++ b/platforms/gke/base/use-cases/federated-learning/terraform/container_image_repository/main.tf
@@ -13,11 +13,11 @@
 # limitations under the License.
 
 resource "google_artifact_registry_repository" "container_image_repository" {
-  location      = var.cluster_region
-  repository_id = "${local.unique_identifier_prefix}-fl-repository"
   description   = "Federated Learning container image repository"
   format        = "DOCKER"
+  location      = var.cluster_region
   project       = google_project_service.artifactregistry_googleapis_com.project
+  repository_id = "${local.unique_identifier_prefix}-fl-repository"
 
   cleanup_policies {
     action = "DELETE"
diff --git a/platforms/gke/base/use-cases/federated-learning/terraform/key_management_service/main.tf b/platforms/gke/base/use-cases/federated-learning/terraform/key_management_service/main.tf
index a0bfd4a0..19cda2ae 100644
--- a/platforms/gke/base/use-cases/federated-learning/terraform/key_management_service/main.tf
+++ b/platforms/gke/base/use-cases/federated-learning/terraform/key_management_service/main.tf
@@ -18,17 +18,17 @@ resource "random_id" "keyring_suffix" {
 }
 
 resource "google_kms_key_ring" "key_ring" {
+  location = var.cluster_region
   name     = "${local.unique_identifier_prefix}-keyring-${random_id.keyring_suffix.hex}"
   project  = google_project_service.cloudkms_googleapis_com.project
-  location = var.cluster_region
 }
 
 resource "google_kms_crypto_key" "cluster_secrects_key" {
-  name                          = "${local.unique_identifier_prefix}-clusterSecretsKey"
+  import_only                   = false
   key_ring                      = google_kms_key_ring.key_ring.id
-  rotation_period               = "7776000s"
+  name                          = "${local.unique_identifier_prefix}-clusterSecretsKey"
   purpose                       = "ENCRYPT_DECRYPT"
-  import_only                   = false
+  rotation_period               = "7776000s"
   skip_initial_version_creation = false
 
   lifecycle {
@@ -45,13 +45,13 @@ resource "google_kms_crypto_key" "cluster_secrects_key" {
 }
 
 resource "google_kms_crypto_key_iam_binding" "cluster_secrets_decrypters" {
-  role          = "roles/cloudkms.cryptoKeyDecrypter"
   crypto_key_id = google_kms_crypto_key.cluster_secrects_key.id
   members       = [local.gke_robot_service_account_iam_email]
+  role          = "roles/cloudkms.cryptoKeyDecrypter"
 }
 
 resource "google_kms_crypto_key_iam_binding" "cluster_secrets_encrypters" {
-  role          = "roles/cloudkms.cryptoKeyEncrypter"
   crypto_key_id = google_kms_crypto_key.cluster_secrects_key.id
   members       = [local.gke_robot_service_account_iam_email]
+  role          = "roles/cloudkms.cryptoKeyEncrypter"
 }
diff --git a/platforms/gke/base/use-cases/federated-learning/terraform/private_google_access/main.tf b/platforms/gke/base/use-cases/federated-learning/terraform/private_google_access/main.tf
index e2babcdd..e527d8ab 100644
--- a/platforms/gke/base/use-cases/federated-learning/terraform/private_google_access/main.tf
+++ b/platforms/gke/base/use-cases/federated-learning/terraform/private_google_access/main.tf
@@ -36,10 +36,10 @@ data "google_compute_network" "main_vpc_network" {
 }
 
 resource "google_dns_managed_zone" "private_google_access" {
-  project     = google_project_service.dns_googleapis_com.project
-  name        = "${local.unique_identifier_prefix}-private-google-apis"
-  dns_name    = "googleapis.com."
   description = "Private DNS zone for Google APIs"
+  dns_name    = "googleapis.com."
+  name        = "${local.unique_identifier_prefix}-private-google-apis"
+  project     = google_project_service.dns_googleapis_com.project
   visibility  = "private"
 
   private_visibility_config {
@@ -50,10 +50,10 @@ resource "google_dns_managed_zone" "private_google_access" {
 }
 
 resource "google_dns_managed_zone" "private_google_access_container_registry" {
-  project     = google_project_service.dns_googleapis_com.project
-  name        = "${local.unique_identifier_prefix}-private-google-access-container-registry"
-  dns_name    = "gcr.io."
   description = "Private DNS zone for Container Registry"
+  dns_name    = "gcr.io."
+  name        = "${local.unique_identifier_prefix}-private-google-access-container-registry"
+  project     = google_project_service.dns_googleapis_com.project
   visibility  = "private"
 
   private_visibility_config {
@@ -64,10 +64,10 @@ resource "google_dns_managed_zone" "private_google_access_container_registry" {
 }
 
 resource "google_dns_managed_zone" "private_google_access_artifact_registry" {
-  project     = google_project_service.dns_googleapis_com.project
-  name        = "${local.unique_identifier_prefix}-private-google-access-artifact-registry"
-  dns_name    = "pkg.dev."
   description = "Private DNS zone for Artifact Registry"
+  dns_name    = "pkg.dev."
+  name        = "${local.unique_identifier_prefix}-private-google-access-artifact-registry"
+  project     = google_project_service.dns_googleapis_com.project
   visibility  = "private"
 
   private_visibility_config {
@@ -79,10 +79,11 @@ resource "google_dns_managed_zone" "private_google_access_artifact_registry" {
 
 resource "google_dns_record_set" "private_google_access_cname" {
   managed_zone = google_dns_managed_zone.private_google_access.name
-  project      = google_project_service.dns_googleapis_com.project
   name         = "*.${google_dns_managed_zone.private_google_access.dns_name}"
-  type         = "CNAME"
+  project      = google_project_service.dns_googleapis_com.project
   ttl          = 300
+  type         = "CNAME"
+
   rrdatas = [
     google_dns_record_set.private_google_access_a.name,
   ]
@@ -90,19 +91,21 @@ resource "google_dns_record_set" "private_google_access_cname" {
 
 resource "google_dns_record_set" "private_google_access_a" {
   managed_zone = google_dns_managed_zone.private_google_access.name
-  project      = google_project_service.dns_googleapis_com.project
   name         = "private.${google_dns_managed_zone.private_google_access.dns_name}"
-  type         = "A"
+  project      = google_project_service.dns_googleapis_com.project
   ttl          = 300
-  rrdatas      = local.private_google_access_ips
+  type         = "A"
+
+  rrdatas = local.private_google_access_ips
 }
 
 resource "google_dns_record_set" "private_google_access_container_registry_cname" {
   managed_zone = google_dns_managed_zone.private_google_access_container_registry.name
-  project      = google_project_service.dns_googleapis_com.project
   name         = "*.${google_dns_managed_zone.private_google_access_container_registry.dns_name}"
-  type         = "CNAME"
+  project      = google_project_service.dns_googleapis_com.project
   ttl          = 300
+  type         = "CNAME"
+
   rrdatas = [
     google_dns_record_set.private_google_access_container_registry_a.name,
   ]
@@ -110,19 +113,21 @@ resource "google_dns_record_set" "private_google_access_container_registry_cname
 
 resource "google_dns_record_set" "private_google_access_container_registry_a" {
   managed_zone = google_dns_managed_zone.private_google_access_container_registry.name
-  project      = google_project_service.dns_googleapis_com.project
   name         = google_dns_managed_zone.private_google_access_container_registry.dns_name
-  type         = "A"
+  project      = google_project_service.dns_googleapis_com.project
   ttl          = 300
-  rrdatas      = local.private_google_access_ips
+  type         = "A"
+
+  rrdatas = local.private_google_access_ips
 }
 
 resource "google_dns_record_set" "private_google_access_artifact_registry_cname" {
   managed_zone = google_dns_managed_zone.private_google_access_artifact_registry.name
-  project      = google_project_service.dns_googleapis_com.project
   name         = "*.${google_dns_managed_zone.private_google_access_artifact_registry.dns_name}"
-  type         = "CNAME"
+  project      = google_project_service.dns_googleapis_com.project
   ttl          = 300
+  type         = "CNAME"
+
   rrdatas = [
     google_dns_record_set.private_google_access_artifact_registry_a.name,
   ]
@@ -130,9 +135,10 @@ resource "google_dns_record_set" "private_google_access_artifact_registry_cname"
 
 resource "google_dns_record_set" "private_google_access_artifact_registry_a" {
   managed_zone = google_dns_managed_zone.private_google_access_artifact_registry.name
-  project      = google_project_service.dns_googleapis_com.project
   name         = google_dns_managed_zone.private_google_access_artifact_registry.dns_name
-  type         = "A"
+  project      = google_project_service.dns_googleapis_com.project
   ttl          = 300
-  rrdatas      = local.private_google_access_ips
+  type         = "A"
+
+  rrdatas = local.private_google_access_ips
 }
diff --git a/platforms/gke/base/use-cases/federated-learning/terraform/service_account/.terraform.lock.hcl b/platforms/gke/base/use-cases/federated-learning/terraform/service_account/.terraform.lock.hcl
new file mode 100644
index 00000000..bfed9458
--- /dev/null
+++ b/platforms/gke/base/use-cases/federated-learning/terraform/service_account/.terraform.lock.hcl
@@ -0,0 +1,22 @@
+# This file is maintained automatically by "terraform init".
+# Manual edits may be lost in future updates.
+
+provider "registry.terraform.io/hashicorp/google" {
+  version     = "6.12.0"
+  constraints = "6.12.0"
+  hashes = [
+    "h1:rvZHMkoxkHrBYQXb/waoZiD2oo3FS1AF8HoWHlb6SN8=",
+    "zh:14701aa307a832d99f567b8056a4c5e4ee5a403d984c98f024deee7507a3f29c",
+    "zh:344eca00ffb2643c2fa7f52f069b659d50bb4c9369df4cad96ea0fadb54282c8",
+    "zh:5fb57c0acfd4d30a39941900040d5518a909d8c975af0c4366a7bfd0d0bb09a8",
+    "zh:617a77048a5b9aa568e8bc706cc84307a237b2dd0e49709028b283f8bbe42475",
+    "zh:677837a05fefe0342cf4d4bdc494e8fd4d62331cac947820e73df37e8f512688",
+    "zh:7b79f6e02474eef4a1480fc6589afb63ed16b25bf019b6056f9838e2845e2ef8",
+    "zh:7d891fceb5b15e81240d829f42e1a36e4c812bfc1abe7856756e59101932205f",
+    "zh:97f1e0ac799faf382426e070e888fac36b0867597b460dc95b0e7f657de21ba9",
+    "zh:9855f2f2f5919ff6a6a2c982439c910d28c8978ad18cd8f549a5d1ba9b4dc4c3",
+    "zh:ac551367180eb396af2a50244e80243d333d600a76002e29935262d76a02290b",
+    "zh:c354f34e6579933d21a98ce7f31f4ef8aeaceb04cfaedaff6d3f3c0be56b2c79",
+    "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
+  ]
+}
diff --git a/platforms/gke/base/use-cases/federated-learning/terraform/service_account/_cluster.auto.tfvars b/platforms/gke/base/use-cases/federated-learning/terraform/service_account/_cluster.auto.tfvars
new file mode 120000
index 00000000..98a694db
--- /dev/null
+++ b/platforms/gke/base/use-cases/federated-learning/terraform/service_account/_cluster.auto.tfvars
@@ -0,0 +1 @@
+../../../../_shared_config/cluster.auto.tfvars
\ No newline at end of file
diff --git a/platforms/gke/base/use-cases/federated-learning/terraform/service_account/_cluster_variables.tf b/platforms/gke/base/use-cases/federated-learning/terraform/service_account/_cluster_variables.tf
new file mode 120000
index 00000000..00625515
--- /dev/null
+++ b/platforms/gke/base/use-cases/federated-learning/terraform/service_account/_cluster_variables.tf
@@ -0,0 +1 @@
+../../../../_shared_config/cluster_variables.tf
\ No newline at end of file
diff --git a/platforms/gke/base/use-cases/federated-learning/terraform/service_account/_platform.auto.tfvars b/platforms/gke/base/use-cases/federated-learning/terraform/service_account/_platform.auto.tfvars
new file mode 120000
index 00000000..125a652c
--- /dev/null
+++ b/platforms/gke/base/use-cases/federated-learning/terraform/service_account/_platform.auto.tfvars
@@ -0,0 +1 @@
+../../../../_shared_config/platform.auto.tfvars
\ No newline at end of file
diff --git a/platforms/gke/base/use-cases/federated-learning/terraform/service_account/_platform_variables.tf b/platforms/gke/base/use-cases/federated-learning/terraform/service_account/_platform_variables.tf
new file mode 120000
index 00000000..486b3eae
--- /dev/null
+++ b/platforms/gke/base/use-cases/federated-learning/terraform/service_account/_platform_variables.tf
@@ -0,0 +1 @@
+../../../../_shared_config/platform_variables.tf
\ No newline at end of file
diff --git a/platforms/gke/base/use-cases/federated-learning/terraform/service_account/_terraform.auto.tfvars b/platforms/gke/base/use-cases/federated-learning/terraform/service_account/_terraform.auto.tfvars
new file mode 120000
index 00000000..4af8b832
--- /dev/null
+++ b/platforms/gke/base/use-cases/federated-learning/terraform/service_account/_terraform.auto.tfvars
@@ -0,0 +1 @@
+../../../../_shared_config/terraform.auto.tfvars
\ No newline at end of file
diff --git a/platforms/gke/base/use-cases/federated-learning/terraform/service_account/_terraform_variables.tf b/platforms/gke/base/use-cases/federated-learning/terraform/service_account/_terraform_variables.tf
new file mode 120000
index 00000000..d51ad696
--- /dev/null
+++ b/platforms/gke/base/use-cases/federated-learning/terraform/service_account/_terraform_variables.tf
@@ -0,0 +1 @@
+../../../../_shared_config/terraform_variables.tf
\ No newline at end of file
diff --git a/platforms/gke/base/use-cases/federated-learning/terraform/service_account/_uc_federated_learning.auto.tfvars b/platforms/gke/base/use-cases/federated-learning/terraform/service_account/_uc_federated_learning.auto.tfvars
new file mode 120000
index 00000000..0d85666e
--- /dev/null
+++ b/platforms/gke/base/use-cases/federated-learning/terraform/service_account/_uc_federated_learning.auto.tfvars
@@ -0,0 +1 @@
+../_shared_config/uc_federated_learning.auto.tfvars
\ No newline at end of file
diff --git a/platforms/gke/base/use-cases/federated-learning/terraform/service_account/_uc_federated_learning_variables.tf b/platforms/gke/base/use-cases/federated-learning/terraform/service_account/_uc_federated_learning_variables.tf
new file mode 120000
index 00000000..f515b3cc
--- /dev/null
+++ b/platforms/gke/base/use-cases/federated-learning/terraform/service_account/_uc_federated_learning_variables.tf
@@ -0,0 +1 @@
+../_shared_config/uc_federated_learning_variables.tf
\ No newline at end of file
diff --git a/platforms/gke/base/use-cases/federated-learning/terraform/service_account/main.tf b/platforms/gke/base/use-cases/federated-learning/terraform/service_account/main.tf
new file mode 100644
index 00000000..8fbd4f70
--- /dev/null
+++ b/platforms/gke/base/use-cases/federated-learning/terraform/service_account/main.tf
@@ -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.
+
+resource "google_service_account" "federated_learning_service_account" {
+  for_each = toset(local.service_account_names)
+
+  account_id   = lower(each.value)
+  description  = "Terraform-managed service account for the federated learning use case in cluster ${local.cluster_name}"
+  display_name = "${local.cluster_name}-${each.value} service account"
+  project      = google_project_service.iam_googleapis_com.project
+}
diff --git a/platforms/gke/base/use-cases/federated-learning/terraform/service_account/output.tf b/platforms/gke/base/use-cases/federated-learning/terraform/service_account/output.tf
new file mode 100644
index 00000000..633bb7f1
--- /dev/null
+++ b/platforms/gke/base/use-cases/federated-learning/terraform/service_account/output.tf
@@ -0,0 +1,13 @@
+# 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.
diff --git a/platforms/gke/base/use-cases/federated-learning/terraform/service_account/project.tf b/platforms/gke/base/use-cases/federated-learning/terraform/service_account/project.tf
new file mode 100644
index 00000000..cfa64332
--- /dev/null
+++ b/platforms/gke/base/use-cases/federated-learning/terraform/service_account/project.tf
@@ -0,0 +1,24 @@
+# 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.
+
+data "google_project" "default" {
+  project_id = var.cluster_project_id
+}
+
+resource "google_project_service" "iam_googleapis_com" {
+  disable_dependent_services = false
+  disable_on_destroy         = false
+  project                    = data.google_project.default.project_id
+  service                    = "iam.googleapis.com"
+}
diff --git a/platforms/gke/base/use-cases/federated-learning/terraform/service_account/versions.tf b/platforms/gke/base/use-cases/federated-learning/terraform/service_account/versions.tf
new file mode 100644
index 00000000..da72ce3e
--- /dev/null
+++ b/platforms/gke/base/use-cases/federated-learning/terraform/service_account/versions.tf
@@ -0,0 +1,28 @@
+# 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 {
+  required_version = ">= 1.5.7"
+
+  required_providers {
+    google = {
+      source  = "hashicorp/google"
+      version = "6.12.0"
+    }
+  }
+
+  provider_meta "google" {
+    module_name = "cloud-solutions/acp_fl_service_account_deploy-v1"
+  }
+}
diff --git a/test/ci-cd/cloudbuild/uc-federated-learning-terraform.yaml b/test/ci-cd/cloudbuild/uc-federated-learning-terraform.yaml
index 266d6d11..4d9d9424 100644
--- a/test/ci-cd/cloudbuild/uc-federated-learning-terraform.yaml
+++ b/test/ci-cd/cloudbuild/uc-federated-learning-terraform.yaml
@@ -48,7 +48,7 @@ steps:
         export ACP_PLATFORM_CORE_DIR="/workspace/platforms/gke/base/core"
 
         export TF_VAR_cluster_project_id="${PROJECT_ID}"
-        export TF_VAR_platform_name="commit-${SHORT_SHA}-fl"
+        export TF_VAR_platform_name="${SHORT_SHA}-fl"
         export TF_VAR_terraform_project_id="${PROJECT_ID}"
 
         # Deploy the base platform and the use case