Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions .github/workflows/strr-terraform.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: STRR Terraform

on:
workflow_dispatch:
inputs:
action:
description: Terraform action for the dev environment
required: true
type: choice
options:
- plan
- apply
default: plan

permissions:
contents: read
id-token: write

concurrency:
group: strr-terraform-dev
cancel-in-progress: false

jobs:
terraform:
name: Terraform dev - ${{ inputs.action }}
runs-on: ubuntu-latest
environment: dev
timeout-minutes: 30

defaults:
run:
shell: bash
working-directory: terraform

steps:
- name: Checkout repository
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
persist-credentials: false

- name: Authenticate to Google Cloud
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3
with:
workload_identity_provider: projects/331250273634/locations/global/workloadIdentityPools/github-actions-pool/providers/github-actions-provider
service_account: sa-strr-infra@bcrbk9-tools.iam.gserviceaccount.com

- name: Set up Terraform
uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e # v4
with:
terraform_version: 1.10.5
terraform_wrapper: false

- name: Terraform init
run: terraform init -input=false -lockfile=readonly

- name: Terraform format check
run: terraform fmt -check -diff -recursive

- name: Terraform validate
run: terraform validate

- name: Require main for apply
if: inputs.action == 'apply' && github.ref != 'refs/heads/main'
run: exit 1

- name: Terraform plan
run: terraform plan -input=false -lock-timeout=5m -var-file=dev.tfvars -out=tfplan

- name: Terraform apply
if: inputs.action == 'apply'
run: terraform apply -input=false -auto-approve tfplan
Comment on lines +69 to +71
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
.DS_Store

# Terraform working directories
.terraform/

# Logs
logs
*.log
Expand Down
22 changes: 22 additions & 0 deletions terraform/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 37 additions & 0 deletions terraform/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# STRR Terraform

This configuration is intentionally limited to the `bcrbk9-dev` project for the initial transfer of STRR infrastructure ownership.

It adopts the existing STRR email Pub/Sub resources and bulk-validation Eventarc trigger into Terraform stored with the application. Eventarc's generated transport topic and subscription remain managed by Eventarc. The application bucket and Cloud Run services are referenced but are not managed by this configuration.

Terraform state is stored in the platform-managed `strr-tools-terraform-state` GCS bucket with the `strr/dev` prefix. The service account, its IAM, and the state bucket remain managed outside this configuration by `bcgov/bcregistry-sre`.

## Local verification

```bash
terraform init
terraform fmt -check -diff -recursive
terraform validate
terraform plan -var-file=dev.tfvars
```

The import blocks are for the initial adoption of existing dev resources. The first reviewed plan must contain only the expected imports, with no resources added, changed, or destroyed.

## GitHub Actions authentication

The workflow uses keyless Workload Identity Federation with:

- Provider: `projects/331250273634/locations/global/workloadIdentityPools/github-actions-pool/providers/github-actions-provider`
- Service account: `sa-strr-infra@bcrbk9-tools.iam.gserviceaccount.com`
- GitHub environment: `dev`

These resource identifiers are not secrets. SRE-managed IAM restricts the GitHub `dev` environment identity that can impersonate the service account. The service account can write Terraform state and act as the existing Eventarc and Pub/Sub runtime service accounts.

## Initial adoption

1. Run the workflow with `action: plan`.
2. Confirm that the plan contains only the expected imports and reports `0 to add, 0 to change, 0 to destroy`.
3. Run the workflow from `main` with `action: apply` to record the imports in Terraform state.
4. Run `action: plan` again and confirm that Terraform reports no changes.

Changes that add missing environment-parity resources should be reviewed separately from this no-change adoption.
7 changes: 7 additions & 0 deletions terraform/data.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
data "google_project" "current" {
project_id = var.project_id
}

locals {
pubsub_service_agent = "service-${data.google_project.current.number}@gcp-sa-pubsub.iam.gserviceaccount.com"
}
6 changes: 6 additions & 0 deletions terraform/dev.tfvars
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
project_id = "bcrbk9-dev"
region = "northamerica-northeast1"
bulk_validation_requests_bucket = "strr_bulk_validation_requests_dev"
bulk_validation_listener_service = "batch-permit-listener-dev"
eventarc_service_account = "sa-eventarc@bcrbk9-dev.iam.gserviceaccount.com"
email_push_service_account = "sa-pubsub@bcrbk9-dev.iam.gserviceaccount.com"
31 changes: 31 additions & 0 deletions terraform/eventarc.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
resource "google_eventarc_trigger" "bulk_permit_validation" {
name = "bulk-permit-validation-trigger"
location = var.region
project = var.project_id

event_data_content_type = "application/json"
service_account = var.eventarc_service_account

matching_criteria {
attribute = "type"
value = "google.cloud.storage.object.v1.finalized"
}

matching_criteria {
attribute = "bucket"
value = var.bulk_validation_requests_bucket
}

destination {
cloud_run_service {
service = var.bulk_validation_listener_service
region = var.region
path = "/"
}
}
}

import {
to = google_eventarc_trigger.bulk_permit_validation
id = "projects/bcrbk9-dev/locations/northamerica-northeast1/triggers/bulk-permit-validation-trigger"
}
109 changes: 109 additions & 0 deletions terraform/pubsub.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
resource "google_pubsub_topic" "emailer" {
name = "strr-emailer-dev"
project = var.project_id
}

resource "google_pubsub_topic" "emailer_dlq" {
name = "strr-emailer-dlq-dev"
project = var.project_id
}

resource "google_pubsub_topic" "bulk_validation_response" {
name = "strr-bulk-validation-response-dev"
project = var.project_id
}

resource "google_pubsub_subscription" "emailer" {
name = "strr-emailer-sub-dev"
project = var.project_id
topic = google_pubsub_topic.emailer.id

ack_deadline_seconds = 10
message_retention_duration = "604800s"
retain_acked_messages = false

expiration_policy {
ttl = ""
}

dead_letter_policy {
dead_letter_topic = google_pubsub_topic.emailer_dlq.id
max_delivery_attempts = 5
}

push_config {
push_endpoint = "https://strr-email-dev-i2rbretwta-nn.a.run.app"

oidc_token {
service_account_email = var.email_push_service_account
}
}

retry_policy {
minimum_backoff = "10s"
maximum_backoff = "600s"
}
}

resource "google_pubsub_subscription" "emailer_dlq" {
name = "strr-emailer-dlq-sub-dev"
project = var.project_id
topic = google_pubsub_topic.emailer_dlq.id

ack_deadline_seconds = 60
message_retention_duration = "604800s"
retain_acked_messages = false

expiration_policy {
ttl = ""
}
}

# Pub/Sub needs these permissions for dead-letter forwarding and tracking.
resource "google_pubsub_topic_iam_member" "emailer_dlq_publisher" {
project = var.project_id
topic = google_pubsub_topic.emailer_dlq.name
role = "roles/pubsub.publisher"
member = "serviceAccount:${local.pubsub_service_agent}"
}

resource "google_pubsub_subscription_iam_member" "emailer_subscriber" {
subscription = google_pubsub_subscription.emailer.id
role = "roles/pubsub.subscriber"
member = "serviceAccount:${local.pubsub_service_agent}"
}

import {
to = google_pubsub_topic.emailer
id = "projects/bcrbk9-dev/topics/strr-emailer-dev"
}

import {
to = google_pubsub_topic.emailer_dlq
id = "projects/bcrbk9-dev/topics/strr-emailer-dlq-dev"
}

import {
to = google_pubsub_topic.bulk_validation_response
id = "projects/bcrbk9-dev/topics/strr-bulk-validation-response-dev"
}

import {
to = google_pubsub_subscription.emailer
id = "projects/bcrbk9-dev/subscriptions/strr-emailer-sub-dev"
}

import {
to = google_pubsub_subscription.emailer_dlq
id = "projects/bcrbk9-dev/subscriptions/strr-emailer-dlq-sub-dev"
}

import {
to = google_pubsub_topic_iam_member.emailer_dlq_publisher
id = "projects/bcrbk9-dev/topics/strr-emailer-dlq-dev roles/pubsub.publisher serviceAccount:service-382361722867@gcp-sa-pubsub.iam.gserviceaccount.com"
}

import {
to = google_pubsub_subscription_iam_member.emailer_subscriber
id = "projects/bcrbk9-dev/subscriptions/strr-emailer-sub-dev roles/pubsub.subscriber serviceAccount:service-382361722867@gcp-sa-pubsub.iam.gserviceaccount.com"
}
35 changes: 35 additions & 0 deletions terraform/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
variable "project_id" {
description = "The single GCP project managed by this initial dev-only configuration."
type = string

validation {
condition = var.project_id == "bcrbk9-dev"
error_message = "This initial configuration is intentionally limited to bcrbk9-dev."
}
}

variable "region" {
description = "Regional location for STRR Eventarc resources."
type = string
default = "northamerica-northeast1"
}

variable "bulk_validation_requests_bucket" {
description = "GCS bucket that emits finalized-object events for bulk validation."
type = string
}

variable "bulk_validation_listener_service" {
description = "Cloud Run service receiving the bulk validation Eventarc trigger."
type = string
}

variable "eventarc_service_account" {
description = "Existing STRR service account used for Eventarc delivery."
type = string
}

variable "email_push_service_account" {
description = "Existing STRR service account used for authenticated email delivery."
type = string
}
20 changes: 20 additions & 0 deletions terraform/versions.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
terraform {
required_version = "~> 1.10.5"

backend "gcs" {
bucket = "strr-tools-terraform-state"
prefix = "strr/dev"
}

required_providers {
google = {
source = "hashicorp/google"
version = "~> 6.0"
}
}
}

provider "google" {
project = var.project_id
region = var.region
}