about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFranck Cuny <franck@fcuny.net>2022-08-10 17:39:53 -0700
committerFranck Cuny <franck@fcuny.net>2022-08-10 17:42:22 -0700
commit318dfa47f1866a50aa64426b220e27ed26ee444a (patch)
treef4f0d5bdcda041d97cefb94c40842d578d68f1a8
parentref(ops/buildkite): use service account impersonation for GCP (diff)
downloadworld-318dfa47f1866a50aa64426b220e27ed26ee444a.tar.gz
feat(ops/tf): script to create GCP service accounts and set roles
Since I'm using terraform for a few things, I want to store the state in
a GCP bucket. This script takes care of creating the bucket, creating
the service account for terraform, setting the roles, and enabling
impersonation.

The script is (or at least is intended) to be idempotent. If a new
project is created, running will update only what is needed.

Change-Id: Ie92703be6d17749dc76dabcf9e73e7b274e8d2ac
Reviewed-on: https://cl.fcuny.net/c/world/+/711
Reviewed-by: Franck Cuny <franck@fcuny.net>
Tested-by: CI
-rwxr-xr-xops/tf-gcs-init/tf-gcs-init.sh113
1 files changed, 113 insertions, 0 deletions
diff --git a/ops/tf-gcs-init/tf-gcs-init.sh b/ops/tf-gcs-init/tf-gcs-init.sh
new file mode 100755
index 0000000..f675381
--- /dev/null
+++ b/ops/tf-gcs-init/tf-gcs-init.sh
@@ -0,0 +1,113 @@
+#!/usr/bin/env bash
+
+# This script creates a bucket in GCS that will be used to store
+# terraform state. It also creates a service account 'terraform' to
+# perform the actions. It ensures the admin of the account can
+# impersonate the 'terraform' service account, so we don't need to
+# generate keys. The roles for the SA are also set.
+
+# TODO:
+# gcloud projects add-iam-policy-binding fcuny-backups --member="serviceAccount:terraform@fcuny-homelab.iam.gserviceaccount.com" --role="roles/viewer"
+# gcloud projects add-iam-policy-binding fcuny-backups
+# --member="serviceAccount:terraform@fcuny-homelab.iam.gserviceaccount.com"
+# --role="roles/owner"
+# I need to perform some actions on all the projects, not just the
+# first one, need to expand the script for that part.
+
+set -u
+set -e
+set -o pipefail
+
+# I'm the admin of the project
+GCP_ADMIN_ACCOUNT="franck.cuny@gmail.com"
+
+# this is the main project that is used for "core" infra
+GCP_PROJECT="fcuny-homelab"
+GCP_PROJECTS="$(gcloud projects list --format 'value(projectId)')"
+GCS_LOCATION="us-west1"
+GCS_BUCKET_NAME="world-tf-state"
+GCP_SERVICE_ACCOUNT_NAME="terraform"
+GCP_SERVICE_ACCOUNT="${GCP_SERVICE_ACCOUNT_NAME}@${GCP_PROJECT}.iam.gserviceaccount.com"
+GCP_SERVICE_ACCOUNT_ROLES=(
+  "roles/editor"
+  "roles/owner"
+)
+
+function bucket:exist() {
+  if gsutil ls gs://${1} &>/dev/null; then
+    true
+  else
+    false
+  fi
+}
+
+function bucket() {
+  if ! bucket:exist "${GCS_BUCKET_NAME}"; then
+    echo "creating GCS bucket $GCS_BUCKET_NAME ..."
+    (
+      set -x
+      gsutil mb -p ${GCP_PROJECT} -l ${GCS_LOCATION} gs://${GCS_BUCKET_NAME}
+      gsutil versioning set on gs://${GCS_BUCKET_NAME}
+    )
+  else
+    echo "GCS bucket $GCS_BUCKET_NAME already created"
+  fi
+}
+
+function service_account:exist() {
+  if gcloud iam service-accounts describe "${1}" &>/dev/null; then
+    true
+  else
+    false
+  fi
+}
+
+function service_account() {
+  if ! service_account:exist "${GCP_SERVICE_ACCOUNT}"; then
+    echo "creating service account ..."
+    (
+      set -x
+      gcloud iam service-accounts create "${GCP_SERVICE_ACCOUNT_NAME}" --display-name="Terraform Service Account"
+    )
+  else
+    echo "service account already created"
+  fi
+}
+
+function service_account:has_role() {
+  [[ $(gcloud projects get-iam-policy ${1} --flatten=bindings --filter="bindings.members=serviceAccount:${2} AND bindings.role=$3" 2>/dev/null | wc -l) -ne 0 ]]
+}
+
+function service_account:admins_token_creator() {
+  [[ $(gcloud --project=${1} iam service-accounts get-iam-policy ${GCP_SERVICE_ACCOUNT} --flatten=bindings --filter="bindings.members=user:${GCP_ADMIN_ACCOUNT} AND bindings.role=roles/iam.serviceAccountTokenCreator" 2>/dev/null | wc -l) -ne 0 ]]
+}
+
+function roles() {
+  for project in $GCP_PROJECTS; do
+    for role in "${GCP_SERVICE_ACCOUNT_ROLES[@]}"; do
+      if ! service_account:has_role "${project}" "${GCP_SERVICE_ACCOUNT}" "${role}" ; then
+        echo "granting ${role##*/} role to service account for project ${project} ..."
+        (
+          set -x
+          gcloud projects add-iam-policy-binding "${project}" --member="serviceAccount:${GCP_SERVICE_ACCOUNT}" --role="${role}"
+        ) 1>/dev/null
+      else
+        echo "service account already has ${role##*/} role for project ${project}"
+      fi
+    done
+
+    if ! service_account:admins_token_creator "${project}" ; then
+      echo "adding AccountTokenCreator role to admin account for project ${project} ..."
+      (
+        set -x
+        gcloud --project="${project}" iam service-accounts add-iam-policy-binding "${GCP_SERVICE_ACCOUNT}" --member user:${GCP_ADMIN_ACCOUNT} --role="roles/iam.serviceAccountTokenCreator"
+      ) 1>/dev/null
+    else
+      echo "admin account has already AccountTokenCreator role for project ${project}"
+    fi
+  done
+}
+
+bucket
+service_account
+roles