diff --git a/infrastructure/terraform/README.md b/infrastructure/terraform/README.md index 5b5cfb1..53f03d4 100644 --- a/infrastructure/terraform/README.md +++ b/infrastructure/terraform/README.md @@ -31,6 +31,22 @@ Current intent: 3. Add Proxmox inventory/configuration scaffolding and imports later. 4. Introduce Ansible workflow after Terraform-managed inventory is trustworthy. + +## Plan-to-config helper script + +Use `scripts/reconcile_from_plan.sh` to automate Terraform configuration generation from `terraform plan` output (via Terraform's `-generate-config-out`). + +From a Terraform module directory (for example `infrastructure/terraform/docker`): + +```bash +../../scripts/reconcile_from_plan.sh --output-file zz_generated_from_plan.auto.tf +``` + +Notes: +- Best used with an import-first workflow that already contains `import {}` blocks. +- The script writes generated config into a `.auto.tf` file and runs `terraform fmt` on it. +- Always review generated arguments before apply. + ## Safety notes - State files are intentionally gitignored for safety and portability. diff --git a/infrastructure/terraform/scripts/reconcile_from_plan.sh b/infrastructure/terraform/scripts/reconcile_from_plan.sh new file mode 100755 index 0000000..d1d25de --- /dev/null +++ b/infrastructure/terraform/scripts/reconcile_from_plan.sh @@ -0,0 +1,103 @@ +#!/usr/bin/env bash +set -euo pipefail + +usage() { + cat <<'USAGE' +Usage: + reconcile_from_plan.sh [--output-file ] [--] [terraform plan args...] + +Description: + Runs `terraform plan` with `-generate-config-out` and writes the generated + configuration into a tracked Terraform file (default: + `zz_generated_from_plan.auto.tf`). + + This is designed for import-first workflows where `import { ... }` blocks are + present and Terraform can generate missing resource arguments from live + infrastructure. + +Options: + --output-file Destination .tf/.auto.tf file to receive generated + configuration. Default: zz_generated_from_plan.auto.tf + -h, --help Show this help text. + +Examples: + ./reconcile_from_plan.sh + ./reconcile_from_plan.sh --output-file generated_imports.auto.tf -- -var-file=terraform.tfvars +USAGE +} + +output_file="zz_generated_from_plan.auto.tf" +plan_args=() + +while (($# > 0)); do + case "$1" in + --output-file) + if (($# < 2)); then + echo "error: --output-file requires a value" >&2 + exit 1 + fi + output_file="$2" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + --) + shift + plan_args=("$@") + break + ;; + *) + plan_args+=("$1") + shift + ;; + esac +done + +if ! command -v terraform >/dev/null 2>&1; then + echo "error: terraform is not installed or not in PATH" >&2 + exit 1 +fi + +if [[ ! -f "main.tf" ]] && ! compgen -G "*.tf" >/dev/null; then + echo "error: no Terraform configuration (*.tf) found in $(pwd)" >&2 + echo "run this script from a Terraform module directory" >&2 + exit 1 +fi + +generated_tmp="$(mktemp -t terraform-generated-XXXXXX.tf)" +trap 'rm -f "$generated_tmp"' EXIT + +echo "Running: terraform plan -generate-config-out=$generated_tmp ${plan_args[*]-}" +set +e +terraform plan -generate-config-out="$generated_tmp" "${plan_args[@]}" +plan_exit=$? +set -e + +if [[ $plan_exit -ne 0 && $plan_exit -ne 2 ]]; then + echo "error: terraform plan failed with exit code $plan_exit" >&2 + exit "$plan_exit" +fi + +if [[ ! -s "$generated_tmp" ]]; then + echo "No generated configuration was produced." + echo "Tip: ensure you have import blocks and resources eligible for config generation." + exit 0 +fi + +cat > "$output_file" <> "$output_file" + +terraform fmt "$output_file" >/dev/null + +echo "Generated configuration written to: $output_file" +echo "Next step: review this file and run terraform plan again to confirm intent."