**Two gotchas when converting `envvars.sec` from `#!vault2text` to `#!gomplate`:**
1.**`| strings.TrimSpace` every vault read.** Vault often stores files with trailing newlines. Without trim, the `\n` lands in the value and Make dies with `missing separator`:
1.**`| strings.TrimSpace` vault reads that get `-include`d into Make.** Vault often stores values with a trailing newline. When such a value lands in `envvars.sec` / `env.mk` and is `-include`d, that `\n` breaks Make with `missing separator`, so trim it:
**Only trim Make-included scalars — never Secret `data:` payloads.** For PEM keys and certs, openssl and most tools emit a trailing newline and many consumers rely on it; for binary blobs like keytabs (stored `format:base64`) any trim mangles the bytes. Either way `TrimSpace` silently corrupts the secret and breaks byte-for-byte parity with what Vault stored (what `vault2kube` produced) — render `data:` payloads verbatim.
2.**Do NOT wrap vault values in shell quotes.** Make's `-include` doesn't strip quotes — `export FOO='bar'` makes the Make variable `FOO` literally `'bar'`. Use unquoted values (Make's RHS is whitespace-permissive for most values).
Vault binary-blob note: Vault stores binary keytabs as `{format: base64, value: <base64>}`. gomplate's `vault-kv` driver returns the raw bytes (auto-decoded), so `| base64.Encode` re-encodes back to the same base64 string the Secret needs — byte-identical to what `vault2kube` produced.
**Render payloads verbatim — never `| strings.TrimSpace` before `base64.Encode`.** PEM keys and certs are commonly stored with a significant trailing newline; trimming it drops a byte from the decoded payload, so the rendered Secret no longer matches the original and a workload may fail later when it loads a key/cert missing its final `\n`. This mirrors the `vault-read.sh` verbatim-output fix.
### Validate
```bash
@@ -308,6 +311,24 @@ make kc-diff # no output (exit 0) = render matches what's deployed
**Never `cat .deploy_sec/secret.yml`** — it contains real base64-encoded keytabs / PEMs. Use `make kc-diff` instead (shows structural delta, no secret bytes when secret is unchanged).
**When a plain-string payload's trailing newline is significant** (PEM keys, certs — values stored *without*`format:base64`), don't assume `TrimSpace` is safe — confirm the migrated render is byte-identical to the legacy `vault2kube` output. Compare the rendered, decoded length against the value stored in Vault without revealing the bytes, e.g.:
```bash
# stored length in Vault vs. rendered length — must match. Read the stored value
# straight from Vault (independent of vault-read.sh's trailing-newline behavior)
# with jq -j, which adds no newline of its own. Both sides count the same bytes
# only for plain-string values — see the keytab note below for format:base64 blobs.
# Run each pipeline as ONE unit: the jq stage alone prints the raw secret to
# stdout — only the trailing `| wc -c` keeps it off your terminal. Assumes the KV
# field is named `value` (the vault-kv driver's convention); under another key jq
# emits the literal `null` (4 bytes) and the counts silently won't match.
vault kv get -format=json "${SEC_PATH}/my-key" | jq -j'.data.value//.data.data.value' | wc-c
A mismatch of exactly 1 byte is the classic dropped-trailing-newline symptom.
> **Not for `format:base64` blobs (binary keytabs).** Vault stores those as a base64 *string*, so the first command counts the base64 length while the second counts the decoded bytes — the two `wc -c` counts legitimately differ even on a perfect render. A binary keytab carries no significant trailing newline anyway; verify its integrity with `make kc-diff` instead.
## 9. Universal gotchas
**OTICA `render.sh``/*` first-line glob bug.** If a `.tf` file starts with `/*`, bash globs it to root dirs. Workaround: prepend a `#` comment on line 1.