Storage Backend Bucket Good Config

Hey there, I am writing my bootstrap file for creating the initial bucket for the Terraform states.

I am interested to know what good practices and configurations would look like for my bucket.

Here is my script so far, please read the comments between code:

BUCKET_NAME=gs://acmec-testing-1
PROJECT=acmec

gsutil mb \
  # Is Bucket Policy Only this a good idea? 
  -b on \
  -c standard \
  -l us-east1 \
  -p $PROJECT \
  $BUCKET_NAME

# What are good practices around labeling this?
gsutil label set ./labels.json $BUCKET_NAME

# What are your lifecycle configuration if any?
gsutil lifecycle set ./lifecycle.json $BUCKET_NAME

# What permissions should I give to the account used for terraform apply in CI?
gsutil defacl ch -u john.doe@example.com:READ $BUCKET_NAME

# Do I need this?
gsutil versioning set on $BUCKET_NAME

I am new into these DevOps stuff so I am worried to make mistakes that would cost me to leak sensitive information.

Thanks in advance.

❯ gsutil rm -r gs://acmec-testing-1
Removing gs://acmec-testing-1/terraform.tfstate#1583025424297405...
AccessDeniedException: 403 Object 'shp-testing-3/terraform.tfstate' is subject to bucket's retention policy and cannot be deleted, overwritten or archived until 2021-02-28T23:17:04.297256878-08:100:

Basically don’t use any retention policy for terraform stuff, instead use versioning.

Good articles to follow:

Alright, after reading what other people are doing I came up with the following solution.

# Use "gcloud organizations list" to find the organization ID.
ORG_ID=""
# Use "gcloud beta billing accounts list" to find the billing account ID.
BILLING_ACCOUNT=""
PROJECT_ID="terraform-project"
BUCKET_NAME="gs://terraform-bucket"

create_terraform_project() {
  org_id=$1
  project_id=$2
  billing_account=$3

  gcloud projects create ${project_id} \
    --name Terraform \
    --set-as-default \
    --organization ${org_id}

  gcloud beta billing projects link ${project_id} \
    --billing-account ${billing_account}
}

create_terraform_service_account() {
  project_id=$1
  account_name=terraform
  creds=./.tmp/credentials.json
  service_account=${account_name}@${project_id}.iam.gserviceaccount.com

  gcloud iam service-accounts create ${account_name} \
    --display-name "Terraform Admin Account" \
    --project ${project_id}

  gcloud iam service-accounts keys create ${creds} \
    --iam-account ${service_account} \
    --project ${project_id}
}

add_service_account_project_permissions() {
  project_id=$1
  account_name=terraform
  service_account=${account_name}@${project_id}.iam.gserviceaccount.com

  gcloud projects add-iam-policy-binding ${project_id} \
    --member serviceAccount:${service_account} \
    --role roles/viewer

  gcloud projects add-iam-policy-binding ${project_id} \
    --member serviceAccount:${service_account} \
    --role roles/storage.admin
}

add_service_account_org_permissions() {
  org_id=$1
  project_id=$2
  account_name=terraform
  service_account=${account_name}@${project_id}.iam.gserviceaccount.com

  gcloud organizations add-iam-policy-binding ${org_id} \
    --member serviceAccount:${service_account} \
    --role roles/resourcemanager.projectCreator

  gcloud organizations add-iam-policy-binding ${org_id} \
    --member serviceAccount:${service_account} \
    --role roles/billing.user
}

create_bucket() {
  project_id=$1
  bucket_name=$2

  gsutil mb \
    -b on \
    -c standard \
    -l us-east1 \
    -p ${project_id} \
    ${bucket_name}

  gsutil label set ./labels.json ${bucket_name}
  gsutil lifecycle set ./lifecycle.json ${bucket_name}
  gsutil versioning set on ${bucket_name}
}

create_terraform_project $ORG_ID $PROJECT_ID $BILLING_ACCOUNT
create_terraform_service_account $PROJECT_ID
add_service_account_project_permissions $PROJECT_ID
add_service_account_org_permissions $ORG_ID $PROJECT_ID
create_bucket $PROJECT_ID $BUCKET_NAME

I ended up disabling lifecycle, at the end of the day the price for keeping those copies are too small for worth the hassle in the future.