fix(1password): retire the legacy OpenTofu and Github vaults

OpenTofu held one hand-made discord webhook read by the cloudflare R2 billing
alerts. Create that webhook in the discord/community module instead and consume
its url via remote state, the same way grafana already does — no manual secret
at all. cloudflare_notification_policy_webhooks.secret is optional and Cloudflare
formats the payload for discord urls, so it's dropped.

Github held only push-o-matic-app, an SSH-key item duplicating credentials the
github-app module already manages as GITHUB_APP_IMMICH_PUSH_O_MATIC in tf. The
provider can't create SSH-key items, so rather than copy it, point the four
PUSH_O_MATIC_* org secrets at the existing tf item. Its pkcs8 field is a
like-for-like swap for the old .private_key, which is also PKCS#8. Adds the
missing client_id to the github-app module (appended last so the positional
field indices in convert_certificate/converted/certificates stay valid).

Also drops two dead 'OpenTofu' vault data sources that nothing referenced.
This commit is contained in:
Zack
2026-07-21 23:43:35 +01:00
parent cc0144f55e
commit 4c962a1114
11 changed files with 52 additions and 27 deletions
@@ -9,6 +9,19 @@ output "leadership_alerts_grafana_webhook_url" {
sensitive = true sensitive = true
} }
// Consumed by cloudflare/account/alerts.tf for the R2 billing notification
// policies. Previously a hand-created Discord webhook stored in the legacy
// "OpenTofu" 1Password vault; terraform owns it now so there's no manual secret.
resource "discord_webhook" "leadership_alerts_cloudflare" {
name = "Cloudflare"
channel_id = discord_text_channel.leadership_alerts.id
}
output "leadership_alerts_cloudflare_webhook_url" {
value = discord_webhook.leadership_alerts_cloudflare.url
sensitive = true
}
resource "discord_webhook" "team_alerts_grafana" { resource "discord_webhook" "team_alerts_grafana" {
name = "Grafana" name = "Grafana"
channel_id = discord_text_channel.team_alerts.id channel_id = discord_text_channel.team_alerts.id
@@ -43,5 +43,9 @@ resource "onepassword_item" "github_app_shared" {
label = "owner" label = "owner"
value = module.github-apps.certificates[each.key].owner value = module.github-apps.certificates[each.key].owner
} }
field {
label = "client_id"
value = module.github-apps.certificates[each.key].client_id
}
} }
} }
@@ -1,7 +1,3 @@
data "onepassword_vault" "opentofu" {
name = "OpenTofu"
}
data "onepassword_vault" "kubernetes" { data "onepassword_vault" "kubernetes" {
name = "Kubernetes" name = "Kubernetes"
} }
@@ -1,12 +1,3 @@
data "onepassword_vault" "opentofu_vault" {
name = "OpenTofu"
}
data "onepassword_item" "discord_leadership_alerts" {
title = "discord-leadership-alerts-webhook"
vault = data.onepassword_vault.opentofu_vault.name
}
locals { locals {
alerts_email = "alerts@immich.app" alerts_email = "alerts@immich.app"
} }
@@ -77,9 +68,12 @@ resource "cloudflare_notification_policy" "r2_class_b_operations_alert" {
} }
} }
// The webhook is created by the discord/community module rather than being a
// hand-made webhook stashed in 1Password. `secret` is omitted deliberately:
// it's optional, and Cloudflare detects the discord URL and formats the payload
// for it, so the cf-webhook-auth header served no purpose here.
resource "cloudflare_notification_policy_webhooks" "discord_leadership_alert" { resource "cloudflare_notification_policy_webhooks" "discord_leadership_alert" {
account_id = var.cloudflare_account_id account_id = var.cloudflare_account_id
name = "Discord Leadership Alerts" name = "Discord Leadership Alerts"
url = data.onepassword_item.discord_leadership_alerts.hostname url = data.terraform_remote_state.discord_community.outputs.leadership_alerts_cloudflare_webhook_url
secret = data.onepassword_item.discord_leadership_alerts.credential
} }
@@ -6,3 +6,12 @@ data "terraform_remote_state" "api_keys_state" {
schema_name = "prod_cloudflare_api_keys" schema_name = "prod_cloudflare_api_keys"
} }
} }
data "terraform_remote_state" "discord_community" {
backend = "pg"
config = {
conn_str = var.tf_state_postgres_conn_str
schema_name = "prod_discord_community"
}
}
@@ -11,5 +11,5 @@ include "root" {
} }
dependencies { dependencies {
paths = ["../api-keys"] paths = ["../api-keys", "../../../scoped/discord/community"]
} }
@@ -1,7 +1,3 @@
data "onepassword_vault" "opentofu" {
name = "OpenTofu"
}
data "onepassword_vault" "kubernetes" { data "onepassword_vault" "kubernetes" {
name = "Kubernetes" name = "Kubernetes"
} }
@@ -25,10 +25,6 @@ data "terraform_remote_state" "cloudflare_account" {
} }
} }
data "onepassword_vault" "github" {
name = "Github"
}
data "onepassword_vault" "tf" { data "onepassword_vault" "tf" {
name = "tf" name = "tf"
} }
@@ -42,9 +42,14 @@ import {
id = "CLOUDFLARE_TILES_R2_KV_TOKEN_HASHED_VALUE" id = "CLOUDFLARE_TILES_R2_KV_TOKEN_HASHED_VALUE"
} }
// Sourced from the github-app module's converted item in the tf vault, which
// already holds this app's credentials. This replaces the duplicate
// "push-o-matic-app" SSH-key item that lived in the single-item "Github" vault
// (both now retired). pkcs8 is a like-for-like swap for the old item's
// .private_key attribute, which the provider also returns as PKCS#8.
data "onepassword_item" "push_o_matic_app" { data "onepassword_item" "push_o_matic_app" {
title = "push-o-matic-app" title = "GITHUB_APP_IMMICH_PUSH_O_MATIC"
vault = data.onepassword_vault.github.name vault = data.onepassword_vault.tf.name
} }
locals { locals {
@@ -52,6 +57,7 @@ locals {
app_id = [for field in data.onepassword_item.push_o_matic_app.section[0].field : field.value if field.label == "app_id"][0] app_id = [for field in data.onepassword_item.push_o_matic_app.section[0].field : field.value if field.label == "app_id"][0]
client_id = [for field in data.onepassword_item.push_o_matic_app.section[0].field : field.value if field.label == "client_id"][0] client_id = [for field in data.onepassword_item.push_o_matic_app.section[0].field : field.value if field.label == "client_id"][0]
installation_id = [for field in data.onepassword_item.push_o_matic_app.section[0].field : field.value if field.label == "installation_id"][0] installation_id = [for field in data.onepassword_item.push_o_matic_app.section[0].field : field.value if field.label == "installation_id"][0]
pkcs8 = [for field in data.onepassword_item.push_o_matic_app.section[0].field : field.value if field.label == "pkcs8"][0]
} }
} }
@@ -79,7 +85,7 @@ import {
resource "github_actions_organization_secret" "push_o_matic_app_key" { resource "github_actions_organization_secret" "push_o_matic_app_key" {
secret_name = "PUSH_O_MATIC_APP_KEY" secret_name = "PUSH_O_MATIC_APP_KEY"
plaintext_value = data.onepassword_item.push_o_matic_app.private_key plaintext_value = local.push_o_matic_fields.pkcs8
visibility = "all" visibility = "all"
} }
@@ -18,6 +18,7 @@ output "certificates" {
app_id = item.section[0].field[2].value app_id = item.section[0].field[2].value
installation_id = item.section[0].field[3].value installation_id = item.section[0].field[3].value
owner = item.section[0].field[4].value owner = item.section[0].field[4].value
client_id = item.section[0].field[5].value
} }
} }
sensitive = true sensitive = true
@@ -34,6 +34,12 @@ resource "onepassword_item" "manual" {
label = "owner" label = "owner"
value = "CHANGE_ME" value = "CHANGE_ME"
} }
// Appended last on purpose: convert_certificate and the converted item below
// index this section positionally, so existing fields must keep their index.
field {
label = "client_id"
value = "CHANGE_ME"
}
} }
lifecycle { lifecycle {
@@ -85,6 +91,10 @@ resource "onepassword_item" "converted" {
label = "owner" label = "owner"
value = each.value.section[0].field[3].value value = each.value.section[0].field[3].value
} }
field {
label = "client_id"
value = each.value.section[0].field[4].value
}
} }
depends_on = [ depends_on = [