#!/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. 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