第1回: エックスサーバー API を curl + jq で叩く — 読み取り系スクリプトの設計

TL;DR

  • エックスサーバーの REST API は Bearer トークンの素朴な作り。
  • .env.xserver に API キーを 1 つ置いて set -a; source; set +a で export、curl -H "Authorization: Bearer ..."xs_get() でラップすれば、ドメイン / SSL / DNS / Cron / DB / SSH 鍵を全部 1 つのスクリプトから読める。
  • 生の JSON を人間が読むなら jq -r '... | "\(.a)\t\(.b)"' | column -t -s $'\t' の組み合わせが鉄板。
  • SSL の common_name、DNS の長い TXT、SSH 公開鍵などは jq の中で前処理してから出すと読みやすい。

モチベーション

エックスサーバーは「サーバーパネル」(Web 管理画面) が主役の老舗だけど、2024 年以降は REST API と CLI と MCP Server まで整備されている。個人開発のドメイン (example.com) を運用するにあたって、以下の事情で全部 API + curl 経由に寄せた:

  • 管理画面に毎回ログインするのがだるい
  • 「DNS のレコード何だっけ」「cron 何時に動いてる?」みたいな確認は、CLI で 1 行で出した方が早い
  • AI エージェント (Claude Code) に運用を任せる場面で、API の方が再現性が高い

公式ドキュメントは https://developer.xserver.ne.jp/api/server/ にある。本稿では「読み取り系 (GET のみ、副作用なし)」のスクリプトを設計する。

ステップ1: API キーと環境ファイル

エックスサーバー管理画面 > API キー管理で発行。発行後はキーが二度と表示されないので忘れずに控える。

deploy/.env.xserver (gitignore 対象) はこんな構造:

# Xserver API
XSERVER_API_KEY="xs_xxxxxxxxxxxxxxxxxxxxxxxx"
# 操作対象サーバ (= /v1/server/<ここ>)
XSERVER_TARGET_SERVER="myserver.xsrv.jp"
# SSH (rsync デプロイで使う)
XSERVER_SSH_HOST="myserver"
# 取り扱うドメイン
LP_DOMAIN="example.com"
API_DOMAIN="api.example.com"
APP_DOMAIN="app.example.com"

スクリプト側はこう読み込む:

set -a; source "${ENV_FILE}"; set +a

: "${XSERVER_API_KEY:?XSERVER_API_KEY が未設定}"
: "${XSERVER_TARGET_SERVER:?XSERVER_TARGET_SERVER が未設定 (例: myserver.xsrv.jp)}"

set -a から set +a までの間に source した変数はすべて export される。後で xs_get() 内で ${AUTH} を組み立てるときに環境変数として参照できる。

: "${VAR:?エラーメッセージ}" は「VAR が未設定なら表示して即終了」のイディオム。最初に書いておくと「キーがないのに走り始めた」事故を防げる。

ステップ2: 共通の xs_get() ラッパー

全エンドポイントで共通する「ベース URL」と「Bearer ヘッダ」を関数化する。

BASE="https://api.xserver.ne.jp/v1/server/${XSERVER_TARGET_SERVER}"
AUTH="Authorization: Bearer ${XSERVER_API_KEY}"

xs_get() {
  local path="$1"
  curl -fsS -H "${AUTH}" "${BASE}/${path}"
}

curl のオプション:

  • -f — HTTP エラー (4xx/5xx) で非ゼロ終了。set -e と組み合わせれば、xs_get が失敗した時点でスクリプト全体が止まる。
  • -s — プログレスバー非表示。
  • -S — ただしエラーメッセージは出す (-s で隠れたものを復活)。

これだけで「xs_get domain | jq .」「xs_get ssl | jq .」みたいに端的に書ける。

ステップ3: jq で見やすく整形する

GET の生レスポンスはどれも JSON。jq-r (raw 出力) でタブ区切りに整形し、column -t -s $'\t' で揃える、というのが汎用パターン。

ドメイン一覧

show_domain() {
  print_header "ドメイン一覧"
  xs_get "domain" | jq -r '
    .domains[] |
    "\(.domain)\ttype=\(.type)\tssl=\(.ssl)\tmemo=\(.memo // "-")"
  ' | column -t -s $'\t'
}

memo // "-" で null/未設定を - に置換。地味だけど一覧の見やすさに効く。

SSL 一覧 (ドメインで絞り込み)

show_ssl() {
  print_header "SSL 証明書 (example.com 関連のみハイライト)"
  xs_get "ssl" | jq -r --arg dom "$LP_DOMAIN" '
    .ssl_list[]
    | select(.common_name | endswith($dom) or . == $dom or contains($dom))
    | "\(.common_name)\t\(.type)\texpires=\(.expires_at)\t\(.status)"
  ' | column -t -s $'\t'
}

--arg で外側の変数を jq に渡す。endswith($dom) or . == $dom or contains($dom) の 3 段重ねは「app.example.com」「example.com」「ワイルドカード *.example.com」をすべてヒットさせるため。

DNS (長い TXT を 80 字でトリム)

DKIM の TXT レコードは平気で 300 文字を超えるので、そのまま出すと見づらい。jq で先頭 80 文字 + に丸める:

xs_get "dns" | jq -r --arg dom "$LP_DOMAIN" '
  .records[]
  | select(.domain == $dom)
  | (.content | if length > 80 then .[0:80] + "…" else . end) as $c
  | "\(.host)\t\(.type)\t\($c)\tttl=\(.ttl)"
' | column -t -s $'\t'

文字列を変数 $c に束ねてからフォーマット文字列に埋め込む、という jq の常套句。

SSH 公開鍵 (末尾 12 文字を short id に)

公開鍵を全部出すと画面が崩れるので、ssh-ed25519 AAAA...XYZ user@host の中から「鍵の末尾 12 文字」と「comment」だけ抜く:

echo "$json" | jq -r '
  .keys[] |
  ((.public_key // "") | split(" ")) as $parts |
  ($parts[2] // "-") as $comment |
  (if ($parts[1] // "" | length) > 12 then ($parts[1] | .[-12:]) else "-" end) as $tail |
  "id=\(.id)\tstatus=\(.status)\tlabel=\(.label)\tcomment=\($comment)\tkey…\($tail)\tat=\(.created_at)"
' | column -t -s $'\t'

公開鍵自体は管理画面でしか見られなくても、複数鍵を見分けるなら末尾 12 文字 + comment で十分。

ステップ4: サブコマンドで切り替える

「全部一気に見たい時」と「DNS だけ見たい時」が両方ある。Bash の case で素朴に振り分け:

case "${1:-all}" in
  domain) show_domain ;;
  ssl)    show_ssl ;;
  dns)    show_dns ;;
  cron)   show_cron ;;
  db)     show_db ;;
  ssh)    show_ssh ;;
  raw)    shift; show_raw "$@" ;;
  all)    show_domain; show_ssl; show_dns; show_cron; show_db; show_ssh ;;
  *)
    echo "使い方: $0 [all|domain|ssl|dns|cron|db|ssh|raw <ep>]" >&2
    exit 2
    ;;
esac

raw <endpoint> を残しておくと、まだスクリプトに組み込んでいないエンドポイントも bash xs-info.sh raw mail で叩けるので便利。

完成形: xs-info.sh

最終的なスクリプトの全文は約 140 行。本連載末尾でリポジトリリンクをまとめて案内する。

呼び出し例:

$ bash deploy/scripts/xs-info.sh domain

== ドメイン一覧 ==
example.com      type=独自ドメイン  ssl=ON  memo=-
api.example.com  type=サブドメイン  ssl=ON  memo=-
app.example.com  type=サブドメイン  ssl=ON  memo=-

$ bash deploy/scripts/xs-info.sh ssl

== SSL 証明書 (example.com 関連のみハイライト) ==
example.com      LE  expires=2026-08-30T00:00:00+09:00  active
api.example.com  LE  expires=2026-09-02T00:00:00+09:00  active
app.example.com  LE  expires=2026-09-02T00:00:00+09:00  active

まとめと次回予告

  • API キーを .env.xserver に置いて、set -a; source; set +a で export。
  • xs_get() 関数で共通ヘッダを 1 箇所に集約。
  • jq -r '...\t...' | column -t -s $'\t' が「読みやすい一覧」の鉄板。
  • 長文 (TXT) や複合フィールド (SSH 公開鍵) は jq の中で前処理してから出す。

ここまでは GET のみで「読むだけ」だった。次回からは POST で 副作用ありの API を安全に叩く ための設計に入る。第 2 回は「DB + ユーザ + 権限付与」を 3 連 POST で自動化する xs-db-create.sh を題材に、DRY-RUN を既定にする運用パターンを扱う。

コメント

タイトルとURLをコピーしました