Add client onboarding script with Semaphore API integration

This commit is contained in:
Semaphore
2026-03-10 17:27:06 -07:00
parent fb8ea52d0f
commit 876a5010e5

227
scripts/onboard_client.sh Executable file
View File

@@ -0,0 +1,227 @@
#!/bin/bash
set -e
# ─── Usage ───────────────────────────────────────────────────────────────────
usage() {
echo "Usage: $0 [options]"
echo ""
echo "Options:"
echo " -i, --id Client ID (e.g. DFA-001)"
echo " -n, --name Client name (e.g. 'DFA Tech Colo')"
echo " -s, --slug Short slug for filenames (e.g. dfa_tech)"
echo " -w, --webhook n8n webhook URL"
echo " -b, --billing Billing model (hybrid|per-host|time-saved|tiered) default: hybrid"
echo " -e, --estimate Human time estimate seconds (default: 2700)"
echo " -v, --vpn VPN type (ipsec|openvpn) default: ipsec"
echo " -h, --hypervisor Hypervisor type (proxmox|xcpng) default: proxmox"
echo " --semaphore-url Semaphore base URL (default: http://localhost:3000)"
echo " --semaphore-token Semaphore API token"
echo ""
echo "Example:"
echo " $0 -i DFA-001 -n 'DFA Tech Colo' -s dfa_tech -w https://n8n.voice1.me/webhook/xxx"
exit 1
}
# ─── Defaults ────────────────────────────────────────────────────────────────
BILLING="hybrid"
ESTIMATE="2700"
VPN="ipsec"
HYPERVISOR="proxmox"
SEMAPHORE_URL="http://localhost:3000"
REPO_DIR="/opt/ansible-msp-automations"
# ─── Parse args ──────────────────────────────────────────────────────────────
while [[ $# -gt 0 ]]; do
case $1 in
-i|--id) CLIENT_ID="$2"; shift 2 ;;
-n|--name) CLIENT_NAME="$2"; shift 2 ;;
-s|--slug) CLIENT_SLUG="$2"; shift 2 ;;
-w|--webhook) WEBHOOK_URL="$2"; shift 2 ;;
-b|--billing) BILLING="$2"; shift 2 ;;
-e|--estimate) ESTIMATE="$2"; shift 2 ;;
-v|--vpn) VPN="$2"; shift 2 ;;
-h|--hypervisor) HYPERVISOR="$2"; shift 2 ;;
--semaphore-url) SEMAPHORE_URL="$2"; shift 2 ;;
--semaphore-token) SEMAPHORE_TOKEN="$2"; shift 2 ;;
*) usage ;;
esac
done
# ─── Validate required args ──────────────────────────────────────────────────
if [[ -z "$CLIENT_ID" || -z "$CLIENT_NAME" || -z "$CLIENT_SLUG" || -z "$WEBHOOK_URL" ]]; then
echo "ERROR: --id, --name, --slug, and --webhook are required"
usage
fi
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " Onboarding client: $CLIENT_NAME ($CLIENT_ID)"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
# ─── Step 1: Generate SSH key ─────────────────────────────────────────────────
echo ""
echo "[ 1/5 ] Generating SSH key..."
KEY_FILE="/root/.ssh/client_${CLIENT_SLUG}"
if [[ -f "$KEY_FILE" ]]; then
echo " ⚠ Key already exists at $KEY_FILE — skipping generation"
else
ssh-keygen -t ed25519 -C "ansible-${CLIENT_SLUG}" -f "$KEY_FILE" -N ""
echo " ✓ Key generated: $KEY_FILE"
fi
echo ""
echo " ┌─ Deploy this public key to all client hosts ───────────────────────"
echo " │"
cat "$KEY_FILE.pub" | sed 's/^/ │ /'
echo " │"
echo " └────────────────────────────────────────────────────────────────────"
# ─── Step 2: Create inventory from template ───────────────────────────────────
echo ""
echo "[ 2/5 ] Creating inventory from template..."
INVENTORY_DIR="$REPO_DIR/inventories/client_${CLIENT_SLUG}"
if [[ -d "$INVENTORY_DIR" ]]; then
echo " ⚠ Inventory directory already exists — skipping"
else
cp -r "$REPO_DIR/inventories/client_template" "$INVENTORY_DIR"
# Write hosts.yml
cat > "$INVENTORY_DIR/hosts.yml" << HOSTSEOF
---
all:
vars:
client_id: "${CLIENT_ID}"
client_name: "${CLIENT_NAME}"
billing_model: "${BILLING}"
maintenance_window_start: "02:00"
maintenance_window_end: "05:00"
maintenance_window_tz: "UTC"
change_freeze: false
hypervisor_type: "${HYPERVISOR}"
vpn_type: "${VPN}"
auto_reboot: false
human_estimate_seconds: ${ESTIMATE}
children:
linux_hosts:
hosts: {}
vars:
ansible_user: root
os_family: "debian"
windows_hosts:
hosts: {}
vars:
ansible_user: Administrator
ansible_connection: winrm
ansible_winrm_transport: ntlm
ansible_winrm_server_cert_validation: validate
ansible_port: 5986
HOSTSEOF
# Write group_vars/all.yml
cat > "$INVENTORY_DIR/group_vars/all.yml" << VARSEOF
---
# Client: ${CLIENT_NAME} (${CLIENT_ID})
# Onboarded: $(date +%Y-%m-%d)
# VPN: ${VPN}
# Hypervisor: ${HYPERVISOR}
# Billing: ${BILLING}
# Add client-specific overrides below
VARSEOF
echo " ✓ Inventory created at $INVENTORY_DIR"
fi
# ─── Step 3: Commit and push to Gitea ────────────────────────────────────────
echo ""
echo "[ 3/5 ] Committing to Gitea..."
cd "$REPO_DIR"
git add .
git commit -m "Onboard client: ${CLIENT_NAME} (${CLIENT_ID}) — inventory scaffold" \
|| echo " ⚠ Nothing to commit"
git push origin main
echo " ✓ Pushed to Gitea"
# ─── Step 4: Create Semaphore project via API ─────────────────────────────────
echo ""
echo "[ 4/5 ] Creating Semaphore project via API..."
if [[ -z "$SEMAPHORE_TOKEN" ]]; then
echo " ⚠ No --semaphore-token provided — skipping Semaphore API setup"
echo " Create the project manually in Semaphore UI"
else
# Create project
PROJECT_RESPONSE=$(curl -s -X POST "$SEMAPHORE_URL/api/projects" \
-H "Authorization: Bearer $SEMAPHORE_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"name\": \"Client - ${CLIENT_NAME}\", \"alert\": false, \"max_parallel_tasks\": 0}")
PROJECT_ID=$(echo "$PROJECT_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])" 2>/dev/null)
if [[ -z "$PROJECT_ID" ]]; then
echo " ✗ Failed to create project — check token and Semaphore URL"
echo " Response: $PROJECT_RESPONSE"
else
echo " ✓ Project created (ID: $PROJECT_ID)"
# Add gitea-deploy key to project key store
GITEA_KEY=$(cat /root/.ssh/gitea_ansible)
curl -s -X POST "$SEMAPHORE_URL/api/project/$PROJECT_ID/keys" \
-H "Authorization: Bearer $SEMAPHORE_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"name\": \"gitea-deploy\", \"type\": \"ssh\", \"ssh\": {\"private_key\": $(echo "$GITEA_KEY" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()))")}}" > /dev/null
echo " ✓ Gitea deploy key added"
# Add client SSH key
CLIENT_KEY=$(cat "$KEY_FILE")
curl -s -X POST "$SEMAPHORE_URL/api/project/$PROJECT_ID/keys" \
-H "Authorization: Bearer $SEMAPHORE_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"name\": \"client-${CLIENT_SLUG}-ssh\", \"type\": \"ssh\", \"ssh\": {\"private_key\": $(echo "$CLIENT_KEY" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()))")}}" > /dev/null
echo " ✓ Client SSH key added to Key Store"
# Add repository
REPO_RESPONSE=$(curl -s -X POST "$SEMAPHORE_URL/api/project/$PROJECT_ID/repositories" \
-H "Authorization: Bearer $SEMAPHORE_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"name\": \"ansible-msp-automations\", \"project_id\": $PROJECT_ID, \"git_url\": \"ssh://git@172.31.10.8:2222/VOICE1/ansible-msp-automations.git\", \"git_branch\": \"main\", \"ssh_key_id\": 1}")
REPO_ID=$(echo "$REPO_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])" 2>/dev/null)
echo " ✓ Repository linked (ID: $REPO_ID)"
# Add variable group
curl -s -X POST "$SEMAPHORE_URL/api/project/$PROJECT_ID/environment" \
-H "Authorization: Bearer $SEMAPHORE_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"name\": \"${CLIENT_SLUG}-vars\", \"project_id\": $PROJECT_ID, \"json\": \"{\\\"N8N_WEBHOOK_URL\\\": \\\"${WEBHOOK_URL}\\\", \\\"CLIENT_ID\\\": \\\"${CLIENT_ID}\\\", \\\"CLIENT_NAME\\\": \\\"${CLIENT_NAME}\\\", \\\"BILLING_MODEL\\\": \\\"${BILLING}\\\", \\\"HUMAN_ESTIMATE_SECONDS\\\": \\\"${ESTIMATE}\\\"}\"}" > /dev/null
echo " ✓ Variable group created"
echo ""
echo " Semaphore project ID: $PROJECT_ID"
echo " Still needed manually:"
echo " - Add inventory (File type → inventories/client_${CLIENT_SLUG}/hosts.yml)"
echo " - Create task templates"
echo " - Add hosts to inventory file in Gitea"
fi
fi
# ─── Step 5: Summary ──────────────────────────────────────────────────────────
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " ✓ Client onboarding complete: $CLIENT_NAME"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo " SSH public key to deploy to client hosts:"
echo " $KEY_FILE.pub"
echo ""
echo " Inventory file to populate with hosts:"
echo " $INVENTORY_DIR/hosts.yml"
echo ""
echo " Next steps:"
echo " 1. Deploy SSH public key to all client hosts"
echo " 2. Add hosts to inventory file in Gitea"
echo " 3. Add inventory entry in Semaphore (File type)"
echo " 4. Create task templates in Semaphore"
echo " 5. Run preflight check"
echo ""