#! /usr/bin/env bash set -euo pipefail vault_args=() temp_vault_pass_file="" usage() { cat <<'EOF' Usage: ./setup [--no-ansible] [--no-terraform|--ansible-only] [clean] [--] [terraform ] Defaults: - Runs Terraform (plan/apply) in terraform/ - Generates Ansible inventory from Terraform outputs - Runs Ansible playbooks Options: --no-ansible Run Terraform only (no Ansible). --no-terraform Skip Terraform; requires existing inventory/hosts.yml. --ansible-only Alias for --no-terraform. clean Remove generated files (inventory, tfplan, cache). --help Show this help. Terraform passthrough: ./setup -- terraform [args] ./setup -- [args] EOF } cleanup() { if [[ -n "${temp_vault_pass_file}" ]] && [[ -f "${temp_vault_pass_file}" ]]; then rm -f "${temp_vault_pass_file}" fi } trap cleanup EXIT ansible_extra_args=() terraform_apply_args=() terraform_passthrough=() run_ansible=true run_terraform=true if [[ "${1:-}" == "--help" ]] || [[ "${1:-}" == "-h" ]]; then usage exit 0 fi if [[ "${1:-}" == "clean" ]]; then echo "Cleaning up generated files..." rm -f inventory/hosts.yml inventory/host_vars/web.yml rm -f terraform/tfplan rm -rf scripts/__pycache__ find . -name ".DS_Store" -delete 2>/dev/null || true echo "Cleanup complete." exit 0 fi if [[ "${1:-}" == "--no-ansible" ]]; then run_ansible=false shift fi if [[ "${1:-}" == "--no-terraform" ]] || [[ "${1:-}" == "--ansible-only" ]]; then run_terraform=false shift fi if [[ "${1:-}" == "--" ]]; then shift if [[ "${1:-}" == "terraform" ]]; then shift terraform_passthrough=("$@") else case "${1:-}" in output|state|workspace|providers|version|validate|fmt|taint|untaint|graph|show|console|import) terraform_passthrough=("$@") ;; *) terraform_apply_args=("$@") ;; esac fi fi if [[ -f ".env" ]]; then set -a source .env set +a fi if [[ "${run_terraform}" == "true" ]]; then if ! command -v terraform >/dev/null 2>&1; then echo "terraform is required (install terraform or run with --no-terraform)" >&2 exit 2 fi fi if [[ "${run_ansible}" == "true" ]]; then if ! command -v ansible-playbook >/dev/null 2>&1; then echo "ansible-playbook is required (install ansible or run with --no-ansible)" >&2 exit 2 fi fi if [[ -f "secrets/vault.yml" ]]; then if ! command -v ansible-vault >/dev/null 2>&1; then echo "ansible-vault is required to read secrets/vault.yml" >&2 exit 2 fi if [[ -f "secrets/.vault_pass" ]]; then vault_args+=(--vault-password-file "secrets/.vault_pass") elif [[ -f ".vault_pass" ]]; then vault_args+=(--vault-password-file ".vault_pass") else read -rsp "Vault password: " vault_password echo temp_vault_pass_file=$(mktemp) chmod 600 "${temp_vault_pass_file}" printf '%s' "${vault_password}" > "${temp_vault_pass_file}" unset vault_password vault_args+=(--vault-password-file "${temp_vault_pass_file}") fi if (( ${#vault_args[@]} )); then vault_plain=$(ansible-vault view secrets/vault.yml "${vault_args[@]}") else vault_plain=$(ansible-vault view secrets/vault.yml) fi while IFS= read -r line; do [[ -z "${line}" ]] && continue [[ "${line}" == "---" ]] && continue [[ "${line}" != TF_VAR_*:* ]] && [[ "${line}" != CF_DNS_API_TOKEN:* ]] && [[ "${line}" != CF_ZONE_API_TOKEN:* ]] && [[ "${line}" != S3_ACCESS_KEY_ID:* ]] && [[ "${line}" != S3_SECRET_ACCESS_KEY:* ]] && continue key="${line%%:*}" value="${line#*:}" value="${value# }" [[ -z "${value}" ]] && continue escaped=$(printf '%q' "${value}") eval "export ${key}=${escaped}" done <<< "${vault_plain}" if [[ -z "${TF_VAR_cloudflare_api_token:-}" ]] && [[ -n "${CF_DNS_API_TOKEN:-}" ]]; then export TF_VAR_cloudflare_api_token="${CF_DNS_API_TOKEN}" fi if [[ -z "${TF_VAR_cloudflare_zone_id:-}" ]] && [[ -n "${CF_ZONE_API_TOKEN:-}" ]]; then export TF_VAR_cloudflare_zone_id="${CF_ZONE_API_TOKEN}" fi fi if [[ "${run_terraform}" == "true" ]]; then init_args=() # Only add backend-config args if S3 backend variables are set if [[ -n "${TF_VAR_tf_state_bucket:-}" ]] && [[ -n "${S3_ACCESS_KEY_ID:-}" ]]; then init_args+=(-backend-config="bucket=${TF_VAR_tf_state_bucket}") init_args+=(-backend-config="region=${TF_VAR_tf_state_region:-us-east-1}") init_args+=(-backend-config="endpoint=${TF_VAR_tf_state_endpoint:-https://us-east-1.linodeobjects.com}") init_args+=(-backend-config="access_key=${S3_ACCESS_KEY_ID}") init_args+=(-backend-config="secret_key=${S3_SECRET_ACCESS_KEY}") # Migrate state from local to S3 init_args+=(-migrate-state) fi terraform -chdir=terraform init ${init_args[@]+"${init_args[@]}"} if (( ${#terraform_passthrough[@]} )); then terraform -chdir=terraform "${terraform_passthrough[@]}" exit 0 fi if (( ${#terraform_apply_args[@]} )); then terraform -chdir=terraform apply "${terraform_apply_args[@]}" else terraform -chdir=terraform plan -out=tfplan terraform -chdir=terraform apply tfplan fi rm -f terraform/tfplan web_ipv4=$(terraform -chdir=terraform output -raw web_ip) web_ipv6=$(terraform -chdir=terraform output -raw web_ipv6) services_ipv4=$(terraform -chdir=terraform output -raw services_ip) ssh_user=${TF_VAR_user:-ansible} mkdir -p inventory/host_vars cat > inventory/hosts.yml < inventory/host_vars/web.yml <&2 exit 2 fi fi if [[ "${run_ansible}" == "true" ]]; then if [[ -n "${vault_args+x}" ]] && (( ${#vault_args[@]} )); then ansible_extra_args=("${vault_args[@]}") fi ansible-playbook playbooks/services.yml ${ansible_extra_args[@]+"${ansible_extra_args[@]}"} ansible-playbook playbooks/web.yml ${ansible_extra_args[@]+"${ansible_extra_args[@]}"} fi