From 318dfa47f1866a50aa64426b220e27ed26ee444a Mon Sep 17 00:00:00 2001 From: Franck Cuny Date: Wed, 10 Aug 2022 17:39:53 -0700 Subject: 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 Tested-by: CI --- ops/tf-gcs-init/tf-gcs-init.sh | 113 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100755 ops/tf-gcs-init/tf-gcs-init.sh 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 -- cgit 1.4.1