tofu: read provider credentials from 1Password
Items are referenced by UUID because the provider fails title lookups
with SDK return code -4.

Assisted-by: Claude Opus 5.5 via Claude Code
change wukvqynwkmvqwpsnpyrsnqnpznorkwyq
commit 049fcf0ab8da3d7f359ff0f6d895d6981628e488
author Alpha Chen <alpha@kejadlen.dev>
date
parent xyvzpkuy
diff --git a/.terraform.lock.hcl b/.terraform.lock.hcl
index 0d218cc..eeb8a80 100644
--- a/.terraform.lock.hcl
+++ b/.terraform.lock.hcl
@@ -1,6 +1,28 @@
 # This file is maintained automatically by "tofu init".
 # Manual edits may be lost in future updates.
 
+provider "registry.opentofu.org/1password/onepassword" {
+  version     = "3.3.1"
+  constraints = "~> 3.3"
+  hashes = [
+    "h1:35PCpSNLVubReT1imwfC+FpIP5gQWx+rvG4njkXkZKM=",
+    "h1:GqvBYImDNYNPMi9o3kJTSIJDeEnNO7om46RWA0wjeso=",
+    "h1:Tg8bJ+ATy5pla6ZTn87lb3nIyiTJTgQHJdxOWQjFF9k=",
+    "h1:a8DHdeyXt8YaEngXGEWZC75W27OG742wtw5BmRnhrpI=",
+    "h1:bPFt8A+PWGwEj3wQ3D0GxyrTMmr1piNrwEuPJu7AZTo=",
+    "h1:nThx/0XctUKlAa6aAsSEHZvx/mD1tSxCE6pacSnotxU=",
+    "h1:syh+iS5VPBQTLszL503BOi5rLpL1wGl1IjyYqgpKYIg=",
+    "zh:02d93a7f520ec69ad8944a68dcbf512e2f9920a6696628b8d05e6ad408309f35",
+    "zh:0f91a902da84470af95f0da4dc21127b84e23c856a431ff9ecfe45d9c6775ef0",
+    "zh:161bc55c466214a5d425ba85753d74ed5078212db965f726e6650d2e1524d633",
+    "zh:3de4a9f212e1046016a3ace8816e2cb15cfb7b9579161e468a08b9034d6a5f51",
+    "zh:730105346065ea3d2bd6acc6f5fe36f7b8a2b54c513d20a46bcd51d656e82bb4",
+    "zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f",
+    "zh:c29f6025d099bc8f1f96e7bb4cd66c5d07209b4141d2fd7720228cf20c9c8efd",
+    "zh:d8ea431d396986ca6baf033fa9aaccb73d6a9f9b7d42bea7af8dc73b9ef20297",
+  ]
+}
+
 provider "registry.opentofu.org/dnsimple/dnsimple" {
   version     = "1.10.0"
   constraints = "~> 1.10"
diff --git a/docs/secrets.md b/docs/secrets.md
index ebbef70..4452080 100644
--- a/docs/secrets.md
+++ b/docs/secrets.md
@@ -62,7 +62,7 @@ sops host_vars/lotus-land-story.sops.yml
 The same command creates a new `host_vars/<host>.sops.yml`, encrypted
 through the creation rule.
 
-## OpenTofu state
+## OpenTofu
 
 OpenTofu keeps state in the gitignored `terraform.tfstate` and encrypts it
 with AES-GCM. The key is 32 random bytes, base64-encoded, in the
@@ -70,6 +70,11 @@ with AES-GCM. The key is 32 random bytes, base64-encoded, in the
 [external key provider][external] that reads it with `op read`, so each
 `tofu` command that touches state asks 1Password for it.
 
+Provider credentials come from the `boxen-*` items in the same vault, read
+through the 1Password provider's ephemeral `onepassword_item` so they never
+reach state. The provider authenticates through the desktop app, which needs
+Settings → Developer → "Integrate with 1Password SDKs" turned on.
+
 [external]: https://opentofu.org/docs/language/state/encryption/
 
 ## Losing the key
diff --git a/main.tf b/main.tf
index 480fae6..35888d4 100644
--- a/main.tf
+++ b/main.tf
@@ -30,6 +30,11 @@ terraform {
       version = "~> 2.18"
     }
 
+    onepassword = {
+      source  = "1Password/onepassword"
+      version = "~> 3.3"
+    }
+
     pihole = {
       source = "ryanwholey/pihole"
 
@@ -44,39 +49,57 @@ terraform {
   }
 }
 
+provider "onepassword" {
+  account = "alydipha.1password.com"
+}
+
+# Items are looked up by UUID: lookups by title fail with SDK return code -4.
+locals {
+  op_private_vault = "7escx6csroqkgfrmkpdubfrqoa"
+}
+
+# boxen-dnsimple
+ephemeral "onepassword_item" "dnsimple" {
+  vault = local.op_private_vault
+  uuid  = "urxqkxvbav5m2trwhzgwkg43pu"
+}
+
+# boxen-linode
+ephemeral "onepassword_item" "linode" {
+  vault = local.op_private_vault
+  uuid  = "wgyjufgbnzfv6h6jvqy7g632ma"
+}
+
+# boxen-pihole
+ephemeral "onepassword_item" "pihole" {
+  vault = local.op_private_vault
+  uuid  = "xpf55644ii3azfcl74tywiagb4"
+}
+
+# boxen-tailscale
+ephemeral "onepassword_item" "tailscale" {
+  vault = local.op_private_vault
+  uuid  = "zoaigxq4d3kg3heqgobxlg4hdu"
+}
+
 provider "dnsimple" {
-  token   = var.dnsimple.token
-  account = var.dnsimple.account
+  token   = ephemeral.onepassword_item.dnsimple.credential
+  account = ephemeral.onepassword_item.dnsimple.username
 }
 
 provider "linode" {
-  # token = LINODE_TOKEN
+  token = ephemeral.onepassword_item.linode.credential
 }
 
 provider "pihole" {
   url      = "http://mountain-of-faith:80"
-  password = var.pihole.password
+  password = ephemeral.onepassword_item.pihole.credential
 }
 
 provider "tailscale" {
-  tailnet = "-"
-  # oauth_client_id = TAILSCALE_OAUTH_CLIENT_ID
-  # oauth_client_secret = TAILSCALE_OAUTH_CLIENT_SECRET
-}
-
-variable "dnsimple" {
-  type = object({
-    token   = string
-    account = string
-  })
-  nullable = false
-}
-
-variable "pihole" {
-  type = object({
-    password = string
-  })
-  nullable = false
+  tailnet             = "-"
+  oauth_client_id     = ephemeral.onepassword_item.tailscale.username
+  oauth_client_secret = ephemeral.onepassword_item.tailscale.credential
 }
 
 data "linode_instances" "ramble_hard" {