feat: create GCS bucket to store remote state

feat: Add gcloud tofu code to automatically create gcs buckets to use as remote backend. Also setup workload identity federation, and store it to infisical, to use it in github actions.

Signed-off-by: Karteek <[email protected]>

feat(tofu): modularise gcs-state

Create a Google Cloud Storage (GCS) bucket and store the state for doing it in the bucket itself

Signed-off-by: Vegard Hagen <[email protected]>

feat(wif): enable workload identify federation

Signed-off-by: Vegard Hagen <[email protected]>
This commit is contained in:
Karteek
2025-07-19 18:54:05 +02:00
committed by Vegard Hagen
parent 7e388f55d7
commit f457ce825f
10 changed files with 192 additions and 0 deletions
+34
View File
@@ -0,0 +1,34 @@
# Google Cloud Storage
Example configuration for creating [Google Cloud Storage](https://cloud.google.com/storage) bucket for storing an
encrypted Tofu state in.
## Prerequisites
Google Cloud Platform project with an active Billing Account.
## Getting started
1. Install `gcloud` — Instructions: https://cloud.google.com/sdk/docs/install-sdk
2. Run `gcloud init`
3. Authenticate using `gcloud auth application-default login`
Before initialising, disable the GCS backend in `providers.tofu` (by e.g. commenting it out) since we can't store the
state in something that doesn't exist yet.
After the initial `tofy apply` to create the bucket, the state can be migrated by adding
```terraform
backend "gcs" {
bucket = var.bucket_name
prefix = var.state_prefix
}
```
back in and running
```shell
tofu init -migrate-state
```
to migrate the state to the remote GCS bucket.
+44
View File
@@ -0,0 +1,44 @@
resource "google_service_account" "tofu_dev_sa" {
account_id = "tofu-dev-sa"
display_name = "Tofu Dev Service Account"
description = "Service account for development tasks using Tofu"
}
# Bucket names must be globally unique.
resource "google_storage_bucket" "tofu_remote_state" {
name = var.bucket_name
location = var.gcp.region
storage_class = "STANDARD"
uniform_bucket_level_access = true
public_access_prevention = "enforced"
versioning {
enabled = true
}
# `force_destroy = true` must be applied before removing bucket
force_destroy = true
# Add a lifecycle rule to delete noncurrent versions after 90 days
lifecycle_rule {
action {
type = "Delete"
}
condition {
# Delete noncurrent versions older than 90 days
age = 30
# Keep up to 100 newer versions
num_newer_versions = 100
# Apply this rule to noncurrent versions
with_state = "ARCHIVED"
}
}
}
# This grants the tofu-dev_sa service account administrative permissions over objects in the bucket.
resource "google_storage_bucket_iam_member" "tofu_remote_state_object_admin" {
bucket = google_storage_bucket.tofu_remote_state.name
role = "roles/storage.objectAdmin"
member = "serviceAccount:${google_service_account.tofu_dev_sa.email}"
}
+7
View File
@@ -0,0 +1,7 @@
output "tofu_service_account" {
value = {
id = google_service_account.tofu_dev_sa.id
name = google_service_account.tofu_dev_sa.name
email = google_service_account.tofu_dev_sa.email
}
}
+24
View File
@@ -0,0 +1,24 @@
terraform {
required_providers {
google = {
source = "hashicorp/google"
version = "~> 6.44.0"
}
}
encryption {
key_provider "pbkdf2" "my_passphrase" {
passphrase = var.tofu_encryption_passphrase
}
method "aes_gcm" "my_method" {
keys = key_provider.pbkdf2.my_passphrase
}
state {
method = method.aes_gcm.my_method
enforced = true
}
plan {
method = method.aes_gcm.my_method
enforced = true
}
}
}
+12
View File
@@ -0,0 +1,12 @@
variable "bucket_name" {
description = "Globally unique name for the GCS bucket"
type = string
}
variable "gcp" {
description = "Google Cloud project ID and region to deploy resources into"
type = object({
project_id = string,
region = string
})
}
+6
View File
@@ -0,0 +1,6 @@
module "gcs" {
source = "./gcs"
gcp = var.gcp
bucket_name = var.bucket_name
}
+4
View File
@@ -0,0 +1,4 @@
output "tofu_dev_service_account_email" {
description = "The email of the service account that GitHub Actions will impersonate. Use this for the GCP_SERVICE_ACCOUNT_EMAIL GitHub secret."
value = module.gcs.tofu_service_account.email
}
+33
View File
@@ -0,0 +1,33 @@
terraform {
required_providers {
google = {
source = "hashicorp/google"
version = "~> 6.44.0"
}
}
encryption {
key_provider "pbkdf2" "encryption_passphrase" {
passphrase = var.encryption_passphrase
}
method "aes_gcm" "encryption_method" {
keys = key_provider.pbkdf2.encryption_passphrase
}
state {
method = method.aes_gcm.encryption_method
enforced = true
}
plan {
method = method.aes_gcm.encryption_method
enforced = true
}
}
backend "gcs" {
bucket = var.bucket_name
prefix = var.state_prefix
}
}
provider "google" {
project = var.gcp.project_id
region = var.gcp.region
}
+4
View File
@@ -0,0 +1,4 @@
gcp = {
project_id = "homelab-359819",
region = "europe-west1"
}
+24
View File
@@ -0,0 +1,24 @@
variable "encryption_passphrase" {
description = "Encryption passphrase for Tofu state encryption"
type = string
sensitive = true
}
variable "gcp" {
description = "Google Cloud project ID and region to deploy resources into"
type = object({
project_id = string,
region = string
})
}
variable "bucket_name" {
description = "Globally unique name for GCS bucket"
type = string
}
variable "state_prefix" {
description = "State prefix in GCS bucket"
type = string
default = "tofu/gcs-state"
}