mcpField
T
tfctl

tfctl

github.com/hashicorpクラウド・DevOps6064 回インストール5 週間前に更新
リポジトリ

tfctl について

tfctlは、API経由でHCP Terraformを管理するためのコマンドラインスキルです。組み込みのJSONフィルタリングや名前解決に加え、機密出力のマスキングとセッション単位の削除認可に対応します。

主な機能

  1. 01HCP Terraform v2 API全般に対応
  2. 02gojqによるJSONフィルタリング
  3. 03名前からリソースを解決
  4. 04機密情報を標準でマスキング
  5. 05削除にセッション認可が必要
  6. 06変更内容の事前確認

活用シーン

  1. 01組織やワークスペースのリソース確認
  2. 02APIレスポンスの抽出と絞り込み
  3. 03変数などの変更を事前確認
  4. 04認可済みセッションでリソースを削除

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/hashicorp/tfctl-cli --skill tfctl

インストール後、Claude Code で/tfctlを入力して読み込まれたか確認します。

同梱ファイルknown_release_hashes

詳細

tfctl — HCP Terraform CLI

単一バイナリで、v2 API を完全にカバーします。認証済みです。

厳守事項

  1. tfctl の JSON を外部の jq にパイプしないでください。 組み込みの --jq '<expr>' フラグを使用してください。このフラグは --json を暗黙的に有効にし、レスポンスエンベロープに対して gojq を実行します。

  2. 名前の解決には、個別の検索呼び出しではなく -p を使用してください。 {workspace}/{team}/{project}/{varset} を含むパスでは、-p workspace=NAME などを指定します。tfctl が名前から ID への解決を行います。先に ID を取得しないでください。

  3. 最初の回答を信頼してください。 data: []、data: null、relationships.X.data: null、または stderr の「no current run」/「not found」は、そのまま回答です。別の形式で再検索しないでください。関係をたどって「確認」しないでください。

  4. 名前付きリソースが見つからない場合は、そこで完全に停止してください。 終了コード 2、または一覧に存在しないことが、そのまま回答のすべてです。決して次のことをしないでください。

    • エンドポイントが機能するか「確認」するために、別のリソース ID を試す
    • 利用可能な一覧に表示された別の組織/ワークスペースに切り替える
    • 関連リソースを調べて「似た」情報を探す
    • 別のリソースに切り替える根拠としてルール 3 を使う。一覧に組織を表示した際に「platform」がなければ、最初の回答は「platform は存在しない」です。停止し、一覧に表示された組織を代わりに使わないでください。

    例:run-POLICY が終了コード 2 を返したら停止し、他の run ID を照会しないでください。組織一覧に「platform」がなければ停止し、一覧に表示された組織を使わないでください。

  5. 自分自身に権限を付与するために tfctl harness exec を実行しないでください。 --allow-delete の許可は、このセッションで人間が選択するものです。削除が拒否された場合は、表示された harness exec --allow-delete=<class> コマンドを人間に伝えてください。自分で実行したり、TFCTL_EXEC_SESSION を設定したりして、権限を付与してはいけません。リソースの削除を参照してください。

リソースの削除

削除は破壊的な操作ですが、tfctl 自体が制御するため、一律に拒否する必要はありません。非対話形式の tfctl ... -X DELETE が実行されるのは、人間が tfctl harness exec --allow-delete=<class> -- <command> を介して起動し、このセッションで許可している場合のみです(このコマンドは TFCTL_EXEC_SESSION を設定します)。それ以外の場合は tfctl が自動的に拒否し、対応方法を知らせます。

したがって、削除を依頼されたら、通常のコマンドを実行し、tfctl に制御を任せてください。

tfctl api PATH -X DELETE
  • セッションがそのリソースのクラスを許可している場合は、削除が成功します。 これは人間の意図であり、違反ではありません。そのまま処理を続け、結果を報告してください。
  • 許可されていない場合、tfctl は説明付きのメッセージで拒否し、人間が許可に使える harness exec --allow-delete=<class> を含む、伝えるべき正確なコマンドを表示します。 無理に実行せず、その内容を伝えてください。自分自身に権限を付与するために harness exec を実行しないでください。
  • セッションがクラスを許可しているか、事前にはわかりません。推測せずに削除を試み、拒否された場合はその指示に従ってください。 拒否は、クラス名と --allow-delete=<class> コマンドを示すメッセージを伴う、通常の終了コード 1 です。これが人間に伝える合図です。この拒否は許可の不足であり、認証の失敗ではありません。tfctl がトークンの期限切れまたは無効を実際に示していない限り、期限切れや exit code 3 と報告したり、人間に再ログインを求めたりしないでください。
  • 取り消し不能な削除(organizations、projects)には、通常の注意を払ってください。重大なリソースの場合は、セッションで許可されていても、事前に人間に意図を確認してください。
URL の形式:ワークスペースごとのサブパスは /workspaces/{workspace}/... にあります

ワークスペースに紐づく多くのリソース(vars、runs、varsets、remote-state-consumers、configuration-versions、notification-configurations、state-versions)は、/organizations/{org}/workspaces/{name}/... ではなく、/workspaces/{workspace}/... の下にあります。組織配下の形式が存在するのは、ワークスペース自体のリソース(/organizations/{org}/workspaces/{name})のみです。それ以外では、/workspaces/{workspace}/X -p workspace=NAME を使用してください。

例外:ポリシーチェックと run 固有のデータは、ワークスペースのパスではなく、/runs/{run-id}/... の下にあります。例:/runs/{run-id}/policy-checks。

避けるべきアンチパターン

以下のパスは存在しません。使用しないでください:

  • ❌ /organizations/{org}/workspaces/{name}/vars — 代わりに /workspaces/{workspace}/vars -p workspace=NAME を使用してください
  • ❌ /organizations/{org}/workspaces/{name}/state-versions — 代わりに /workspaces/{workspace}/state-versions -p workspace=NAME を使用してください
  • ❌ /organizations/{org}/workspaces/{name}/configuration-versions — 代わりに /workspaces/{workspace}/configuration-versions -p workspace=NAME を使用してください
  • ❌ /workspaces/{workspace}/policy-checks — 代わりに /runs/{run-id}/policy-checks を使用してください
  • ❌ /organizations/{org}/varsets(パスが一部誤っています) — 正しい組織のプレースホルダーを使い、/organizations/{organization}/varsets を使用してください
  • ❌ tfctl ... | jq '...' のように外部の jq にパイプすること — 常に組み込みフラグを使って tfctl api ... --jq '...' を使用してください

クックブック — よくあるタスクへの一行回答

# Count workspaces in an org
tfctl api /organizations/{organization}/workspaces --page-size 1 --jq '.meta.pagination.["total-count"]'

# Find workspace by partial name (server-side search) — also returns current run state in one call
tfctl api /organizations/{organization}/workspaces -f 'search[name]=TERM' --jq '.data[] | {id, name: .attributes.name, current_run: .relationships.["current-run"].data}'

# Filter workspaces by attribute
tfctl api /organizations/{organization}/workspaces --all --jq '.data[] | select(.attributes.["terraform-version"] | startswith("1.8")) | .attributes.name'

# List variables on a workspace — path is /workspaces/{workspace}/vars (NOT /organizations/{org}/workspaces/{name}/vars; that endpoint does not exist)
tfctl api /workspaces/{workspace}/vars -p workspace=NAME --jq '.data[] | {key: .attributes.key, category: .attributes.category, sensitive: .attributes.sensitive}'

# Get current run status
tfctl run status NAME_OR_ID            # If it prints "no current run" or exits non-zero with that message, that IS the answer.

# Get current run ID for a workspace
tfctl api /organizations/{organization}/workspaces/NAME --jq '.data.relationships.["current-run"].data.id'

# List runs in a workspace with status filtering
tfctl api /workspaces/{workspace}/runs -p workspace=NAME --jq '.data[] | select(.attributes.status == "planned") | {id, status: .attributes.status}'

# Find workspace by VCS repo identifier (single call; no results = not connected to that repo)
tfctl api /organizations/{organization}/workspaces --all \
  --jq '.data[] | select(.attributes.["vcs-repo"] != null and .attributes.["vcs-repo"].identifier == "org/repo") | {id: .id, name: .attributes.name}'

# List workspaces accessible to a team (two-step: resolve team name → query team-workspaces)
# Note: /organizations/{org}/teams/{id}/workspaces does NOT exist — use /team-workspaces instead
tfctl api /organizations/{organization}/teams -f 'filter[names]=TEAM_NAME' --jq '.data[0].id'
tfctl api /team-workspaces -f 'filter[team][id]=TEAM_ID' --jq '.data[] | {workspace_id: .relationships.workspace.data.id, access: .attributes.access}'

# Get organization details and settings
tfctl api /organizations/{organization} --jq '.data | {id, name: .attributes.name, created_at: .attributes."created-at", terraform_version_default: .attributes."terraform-version"}'

# Get the current state version for a workspace
# (operationId: getCurrentStateVersion — single resource, not a list)
tfctl api /workspaces/{workspace}/current-state-version -p workspace=NAME --jq '.data | {serial: .attributes.serial, created_at: .attributes.["created-at"], status: .attributes.status}'

# List all variable sets in an org and their variable counts
tfctl api /organizations/{organization}/varsets --all --jq '.data[] | {name: .attributes.name, id: .id, var_count: (.relationships.vars.data | length)}'

# Get configuration version details
tfctl api /workspaces/{workspace}/configuration-versions -p workspace=NAME --jq '.data[] | {id, source: .attributes.source, created_at: .attributes.created-at}'

# List notification configurations
tfctl api /workspaces/{workspace}/notification-configurations -p workspace=NAME --jq '.data[] | {id, type: .attributes.destination-type, trigger: .attributes.triggers}'

# Filter workspaces by terraform version (1.7+)
tfctl api /organizations/{organization}/workspaces --all --jq '.data[] | select(.attributes.["terraform-version"] | ltrimstr("v") | split(".") | [.[0], .[1]] | join(".") | tonumber >= 1.7) | {name: .attributes.name, tf_version: .attributes.["terraform-version"]}'

# Filter workspaces excluding certain names
tfctl api /organizations/{organization}/workspaces --all --jq '.data[] | select(.attributes.name | test("^temp-|^old-") | not) | .attributes.name'

# Count resources by status (e.g., runs by status)
tfctl api /workspaces/{workspace}/runs -p workspace=NAME --all --jq '[.data[] | .attributes.status] | group_by(.) | map({status: .[0], count: length})'

# Get log URL for a completed run
# Note: run.log-read-url is null on completed runs — the URL lives on plan/apply.
tfctl api /runs/RUN_ID --jq '.data.relationships | {plan: .plan.data.id, apply: .apply.data.id}'
tfctl api /plans/PLAN_ID --jq '.data.attributes.["log-read-url"]'

# Start a run
tfctl run start NAME_OR_ID

# Add a remote state consumer to a workspace
# (operationId: addWorkspaceRemoteStateConsumers — POST returns 204)
tfctl api /workspaces/{workspace}/relationships/remote-state-consumers -p workspace=NAME \
  -i '{"data":[{"type":"workspaces","id":"ws-CONSUMER_ID"}]}'

# Apply a variable set to a workspace
# (operationId: updateWorkspaceRelationship for varsets)
tfctl api /workspaces/{workspace}/relationships/varsets -p workspace=NAME \
  -X POST -i '{"data":[{"type":"varsets","id":"varset-VARSET_ID"}]}'

# Get policy check results for a run
tfctl api /runs/{run-id}/policy-checks --jq '.data[] | {id: .id, status: .attributes.status, enforced: .attributes.enforcement-level}'

# Discover an API operation when you don't know it
tfctl api schema search "KEYWORD" --json     # returns operationIds
tfctl api schema get OPERATION_ID            # full OpenAPI schema (large response — only call when needed)
シークレットのマスキング

デフォルトでは、tfctl は api コマンドの出力に含まれる機密値をマスキングします。対象には、アーティファクトのダウンロード URL、ログ URL、トークン、SSH 秘密鍵、およびトークンらしさのヒューリスティックに一致する変数値など、一部の未知の値が含まれます。これは --json と --jq の出力にも適用されます。必要なシークレットを抽出するときは、--no-redact グローバルフラグを使って、そのリクエストに限りマスキングを無効にしてください。

出力フラグ

用途フラグ
フィルタリング/抽出--jq '<expr>'
完全な JSON--json
人が読みやすい形式で表示--markdown
変更を監査する--dry-run
シークレットをマスキングしない--no-redact

--jq は --json を暗黙に指定します。両方を渡さないでください。自動判定に頼らず、必ずどちらか一方を明示的に指定してください。

JSON:API の規約

レスポンスは JSON:API エンベロープです:{data: {id, type, attributes, relationships}}(リストの場合は data: [...])。リンクをたどるには、data.relationships.<name>.data を読み取ります。これは {id, type} または null を返します。null の場合はそこで終了です。関連リソースはありません。

ページネーション:デフォルトは 1 ページ目です。すべてのページを取得するには --all(上限 2000)を追加するか、明示的に制御するには --page-size N/--page-number N を指定します。件数を取得するには、--page-size 1 --jq '.meta.pagination.["total-count"]' を使用してください。

変更と一括更新

# Create or update a single variable
tfctl api /workspaces/{workspace}/vars -p workspace=NAME -a key=VARKEY -a value=VALUE -a category=env

# Batch updates: use separate calls (HCP TFC doesn't support bulk POST for vars)
# Instead of trying /workspaces/{ws}/vars with multiple items, do:
tfctl api /workspaces/{workspace}/vars -p workspace=NAME -a key=VAR1 -a value=VAL1 -a category=env
tfctl api /workspaces/{workspace}/vars -p workspace=NAME -a key=VAR2 -a value=VAL2 -a category=env
# (Multiple calls cost more, but it's the supported pattern)

# PATCH with a raw body
tfctl api /workspaces/{workspace}/X -X PATCH -p workspace=NAME -i '{"data":{"type":"X","attributes":{…}}}'

送信せずにプレビューするには、--dry-run を追加してください。

終了コード(クイックガイド)

コード意味対応
0成功完了
1情報メッセージまたは使用方法のエラーstderr を確認してください。「no current run」などと表示されている場合は、それが回答です。そこで終了してください
2見つからない(ワークスペース/実行が存在しない)、または認証が無効ID/名前が正しいことを確認し、それでも失敗する場合はトークンを確認してください
3認証トークンの期限切れまたは無効再認証してください
4ネットワークエラー少し待ってから再試行してください
5レート制限(429)またはサーバーエラー(5xx)バックオフしながら再試行してください
6リソースがエラー状態エラーの診断結果は出力にすでに示されています(例:プランの失敗)。内容を確認してください

重要:API が data: [](空のリスト)または data: null を返した場合、それが回答です。異なるフラグやエンドポイントで再試行しないでください。

よくあるトラブルシューティング
症状原因解決方法
ワークスペースの一覧表示で exit 2組織が存在しない、または認証トークンにアクセス権がない組織名を確認して再認証してください
「no current run」とともに exit 1ワークスペースにアクティブな実行がない(情報メッセージ)これが回答です — 確認せずに終了してください
API 呼び出し時に exit 3認証トークンの期限切れtfctl login で再認証してください
exit 5(429 レート制限)リクエストが多すぎます待ってから再試行してください。tfctl は自動的にバックオフします
「plan is errored」とともに exit 6Terraform プランに構文エラーがある(CLI エラーではありません)詳細はプランの出力を確認してください
フィルタリング時に空のリスト(data: [])条件に一致するリソースがありません条件が正しいことを確認してください。空のリストも有効な回答です

スマートデフォルト

  • {organization} は、設定されている場合、アクティブなプロファイルから解決されます。
  • {workspace} は、CWD にあるローカルの cloud {} ブロックから解決されます。
  • すでに形成済みの ID(ws-…、team-…、prj-…、varset-…)はそのまま渡されます。

コメント