Add --json flag to CLI commands for machine-readable output on stdout. Generic pattern for any CLI tool, enabling piping to jq and CI/CD integration while keeping human-readable output on stderr.
日本語の概要は準備中です。原文の説明を表示しています。
InfraGlue (ig) — CLI tool for managing Infrastructure as Code (IaC) monorepos. Orchestrates Terraform and Pulumi workspaces with dependency resolution, output injection, environment management, drift detection, and state reconciliation. Use this skill when working with ig.yaml configs, ig CLI commands, or multi-workspace Terraform/Pulumi infrastructure projects. Keywords: terraform, pulumi, infrastructure, IaC, monorepo, workspace, ig, infraglue, drift, plan, apply, destroy, import, export, refresh, environment.
インストール方法を見るインストールする前に、エージェントに与えられる指示の中身を確認できます。
InfraGlue orchestrates multiple Terraform and Pulumi workspaces in a single monorepo. It resolves dependencies between workspaces, manages per-environment configuration, and injects outputs from one workspace into another.
ig.yaml at monorepo root defines which directories are workspacesig.yaml with dependencies, injection mappings, and environment configig builds a dependency graph, topologically sorts workspaces into execution levelsIf you are an AI coding agent, read this section first. These rules prevent the most common mistakes agents make with ig.
Let ig orchestrate — do NOT parallelize manually. Run ig apply --approve all or ig destroy --approve all to operate on the full monorepo. ig handles dependency ordering, output injection, and parallelism internally. Do NOT run separate ig apply --project X commands in parallel — this is the single most common agent mistake.
Never run ig commands concurrently. ig uses a process-local mutex to protect .ig/state.json. Running two ig processes at the same time causes race conditions — lost output data, broken dependency injection, and corrupted state. Always wait for one ig command to finish before starting the next.
Select the environment once, then omit --env. For single-environment setups (or when working in one env for a session), run ig env select <env> once at the start. All subsequent commands use the selected env automatically. Only pass --env when you need to switch environments mid-session. For Pulumi workspaces, ig env select creates a stack only when Pulumi explicitly reports that the requested stack is missing; backend, network, or auth errors fail fast so an uncertain state is not treated as a missing stack.
Prefer whole-monorepo commands. ig plan, ig apply, ig destroy, and ig drift all operate across every workspace by default. Only use --project for targeted single-workspace operations like debugging, import, or export.
Use --approve all for apply and destroy. ig auto-detects non-TTY environments and suppresses interactive prompts, but ig apply and ig destroy still require explicit --approve to proceed. Always pass --approve all in agent/CI contexts.
# Correct agent workflow
ig env select dev
ig plan # preview all workspaces
ig apply --approve all # apply all workspaces in dependency order
ig drift # check for drift across all workspaces
ig destroy --approve all # tear down in reverse dependency order
Located at the monorepo root. Defines workspace discovery and global outputs.
workspace:
- './*' # glob patterns matching workspace directories
vars: # optional: shared variables injected into all workspaces at lowest priority
region: us-east-1
env_name: production
output: # optional: expose workspace outputs at monorepo level
postgres_host: './postgres:database_host' # format: './workspace-dir:output_key'
app_url: './express-service:app_url'
| Field | Type | Required | Description |
|---|---|---|---|
workspace | string[] | Yes | Glob patterns to discover workspace directories (must match at least one) |
vars | Record<string, string> | No | Shared variables passed to all workspaces (lowest priority, overridden by env vars and injections) |
output | Record<string, string> | No | Map of exported names to './workspace:output_key' references |
vault | VaultConfig | No | HashiCorp Vault connection settings (see Vault Secret Interpolation) |
Located in each workspace directory. Defines provider, dependencies, and environment config.
provider: terraform # optional: auto-detected from files if omitted
injection: # optional: wire outputs from other workspaces as input variables
network_name: '../docker-network:network_name'
database_host: '../postgres:database_host'
depends_on: # optional: explicit ordering without injection
- '../other-workspace'
alias: my-custom-name # optional: override workspace name (defaults to directory name)
output: # optional: rename/remap provider outputs
public_name: internal_output_key
envs: # per-environment configuration
dev:
backend_type: local
backend_config:
path: ./terraform_dev.tfstate
vars:
instance_count: 1
var_files:
- ./envs/dev.tfvars
qa:
backend_file: ./envs/qa_backend.tf
var_files:
- ./envs/qa.tfvars
prod:
backend_type: s3
backend_config:
bucket: my-terraform-state
key: prod/terraform.tfstate
vars:
instance_count: 3
var_files:
- ./envs/prod.tfvars
| Field | Type | Required | Description |
|---|---|---|---|
provider | string | No | terraform or pulumi. Auto-detected if omitted (from .tf or Pulumi.yaml files) |
injection | Record<string, string> | No | Map of variable names to '../workspace:output_key' references. Creates implicit dependency |
depends_on | string[] | No | Explicit dependencies without output injection. Use relative paths like '../workspace' |
alias | string | No | Custom workspace name (defaults to directory name) |
output | Record<string, string> | No | Remap provider output keys to different names |
envs | Record<string, EnvConfig> | No | Per-environment configuration (see below) |
Each key under envs is an environment name with this structure:
| Field | Type | Description |
|---|---|---|
backend_type | string | Terraform backend type (local, s3, gcs, etc.) |
backend_file | string | Path to a backend configuration file (Terraform .tf file) |
backend_config | Record<string, string> | Backend-specific key-value config. For Pulumi: PULUMI_BACKEND_URL, PULUMI_CONFIG_PASSPHRASE |
vars | Record<string, string> | Variables passed to the provider (Terraform -var, Pulumi config) |
var_files | string[] | Variable files passed to provider (Terraform -var-file) |
Use backend_type + backend_config OR backend_file, not both.
Variables are merged with the following priority (highest wins):
envs.<env>.vars) — overrides root varsvars in root ig.yaml) — lowest priority, shared defaultsThis means a workspace can override root-level defaults by declaring the same variable in its envs.<env>.vars, and injections always take precedence over both.
String values in vars, backend_config, backend_type, backend_file, and var_files support ${ENV_VAR} syntax, resolved from the shell environment at parse time.
# root ig.yaml
vars:
region: ${AWS_REGION}
# workspace ig.yaml
envs:
prod:
backend_type: s3
backend_config:
bucket: ${TF_STATE_BUCKET}
key: prod/terraform.tfstate
vars:
db_host: ${DATABASE_HOST}
var_files:
- ./envs/${AWS_REGION}.tfvars
${VAR} resolves to the value of environment variable VAR$${VAR} escapes to the literal string ${VAR} (no interpolation)workspace, injection, depends_on, alias, provider, output) are NOT interpolatedString values in vars, backend_config, backend_type, backend_file, and var_files support ${vault:path#field} syntax, which fetches secrets from HashiCorp Vault's KV v2 engine at config parse time.
# root ig.yaml
vault:
address: https://vault.example.com # optional, falls back to VAULT_ADDR env var
role: infra-role # optional, for JWT auth — falls back to VAULT_ROLE env var
workspace:
- './*'
# workspace ig.yaml
envs:
prod:
backend_type: azurerm
backend_config:
storage_account_name: ${vault:secret/data/azure#storage_account}
access_key: ${vault:secret/data/azure#access_key}
vars:
db_password: ${vault:secret/data/db/prod#password}
PULUMI_CONFIG_PASSPHRASE: ${vault:secret/data/pulumi#passphrase}
${vault:path#field} fetches field field from the KV v2 secret at pathsecret/data/ prefix (e.g., secret/data/myapp)$${vault:path#field} escapes to the literal string ${vault:path#field} (no resolution)${ENV_VAR} references can be mixed in the same configAuthentication — ig resolves a Vault token using this priority:
VAULT_TOKEN env var (set explicitly or by vault login)~/.vault-token file (written by vault login)VAULT_ID_TOKEN is set, exchanges it for a token via POST /v1/auth/{mount}/loginFor local development, run vault login once. In CI (e.g., GitLab), the pipeline provides VAULT_ID_TOKEN automatically.
Vault config fields:
| Field | Type | Description |
|---|---|---|
address | string | Vault server URL. Falls back to VAULT_ADDR env var |
role | string | Role for JWT auth. Falls back to VAULT_ROLE env var |
ig loads .env files from the .ig/ directory before config interpolation, so values are available for ${VAR} substitution in ig.yaml and for provider subprocesses (Terraform/Pulumi).
Files loaded (in order):
.ig/.env — base variables, overrides process.env.ig/.env.{envName} — per-environment variables, overrides bothMissing files are silently ignored. The .ig/ directory is gitignored by default (its .gitignore contains *), making it safe for secrets and local overrides.
Format:
# Comments and blank lines are ignored
DATABASE_HOST=localhost
DATABASE_PORT=5432
export API_KEY="my-secret-key"
QUOTED_VALUE='single quotes work too'
COMPLEX=value=with=equals
Supported syntax: KEY=VALUE, KEY="quoted", KEY='single quoted', # comments, blank lines, and export prefix.
Example usage:
# .ig/.env — shared across all environments
TF_STATE_BUCKET=my-terraform-state
AWS_REGION=us-east-1
# .ig/.env.prod — production-only overrides
DATABASE_HOST=prod-db.example.com
API_KEY=prod-secret-key
# ig.yaml — references are resolved from loaded .env files
envs:
prod:
backend_config:
bucket: ${TF_STATE_BUCKET}
vars:
db_host: ${DATABASE_HOST}
Workspaces wire outputs from upstream workspaces using the injection field:
# In workspace redis/ig.yaml
injection:
network_name: '../docker-network:network_name'
This means: "take the network_name output from the docker-network workspace and pass it as the network_name variable to this workspace."
injection creates an implicit dependency (no need to also list in depends_on)depends_on is for ordering without data flow--secret (Pulumi) or as sensitive (Terraform). No manual secret marking needed (requires ig >=0.2.1)# Plan and apply infrastructure
ig plan --env dev # preview changes without applying (exit code 2 = changes)
ig plan --env dev --detailed # show attribute-level diffs
ig apply --env dev # apply all workspaces in dev environment
ig apply --env dev --project postgres # apply only the postgres workspace
ig apply --env dev --no-deps # apply without running dependencies
ig apply --env dev --start-with-project postgres # skip upstream levels, use cached outputs
ig destroy --env staging # destroy all workspaces
# GitLab CI (bridge-less mode)
ig ci --env staging # full lifecycle: read approvals, apply, plan, post comments
ig ci --env prod --approval-emoji rocket # use custom emoji for approval
# Drift detection and reconciliation
ig drift --env staging # detect drift across all workspaces (exit code 2 = drift)
ig drift --env prod --json # output structured DriftReport as JSON
ig drift --env dev --project postgres # check single workspace
ig refresh --env staging # refresh state from cloud providers
ig refresh --env dev --project redis # refresh single workspace
ig import aws_instance.web i-123 --project webserver --env staging # import existing resource
ig export aws_instance.web i-123 --project webserver --env staging # generate code to stdout
# Environment management
ig env select dev # select active environment
ig env current # show current environment
# Configuration
ig config show # display parsed monorepo config
ig config show --json # JSON output for scripting
# Provider passthrough (stdout/stderr are passed through directly via stdio: inherit)
ig provider plan # run provider CLI command in current workspace dir
ig provider output -json # get raw provider outputs
# Shell completions
ig completion bash >> ~/.bashrc
ig completion zsh >> ~/.zshrc
ig completion fish > ~/.config/fish/completions/ig.fish
# Install this skill
ig install-skill # install into .claude/skills/infraglue/SKILL.md
ig install-skill --force # overwrite existing
AI agents: see the Agent Usage section above for the correct workflow. The details below are reference for CI pipelines and scripting.
--approve syntax (for ig apply and ig destroy):
| Value | Effect |
|---|---|
all | Auto-approve every execution level |
1 | Auto-approve level 1 only |
1,2,3 | Auto-approve specific levels |
Level numbers are 1-indexed. Without --approve, the command waits for interactive confirmation. Pre-approved levels skip the plan step entirely and apply directly (no terraform plan/pulumi preview), which is faster — especially for Pulumi where preview and up both run the program.
--up-to-level <N> (for ig apply and ig destroy):
Stop execution after level N (1-indexed). Combine with --approve all to auto-apply up to a specific level:
ig apply --env prod --approve all --up-to-level 2 # apply levels 1 and 2, skip the rest
This is used by the GitLab bridge to apply only the approved level.
Environment variables for bridge-triggered pipelines:
When the InfraGlue Bridge triggers a pipeline, it sets IG_ACTION=apply and IG_APPROVED_LEVEL=N. The CLI auto-detects these and defaults to --approve all --up-to-level N (explicit CLI flags take precedence).
| Variable | Description |
|---|---|
IG_ACTION | Set to apply by the bridge |
IG_APPROVED_LEVEL | Level number approved via MR emoji reaction |
IG_PLAN_ID | Plan ID for correlation |
IG_MR_IID | Merge request IID for correlation |
--start-with-project <name> (for ig apply, ig destroy, ig plan):
Skip all execution levels before the level containing the named project. Cached outputs from .ig/state.json are used for skipped workspaces instead of running provider commands. Useful for resuming partially-applied monorepos or iterating on a downstream workspace without re-running upstream.
ig apply so that cached outputs exist in .ig/state.json--project and --no-deps--approve still refer to the original plan levelsExit codes: 0 = success / no changes, 1 = error, 2 = changes detected (plan/drift)
TTY detection: when no TTY is detected (CI, piped output, agent subprocess), ig auto-selects the no-tty-cli integration which suppresses interactive prompts. ig plan and ig drift are always non-interactive.
During ig apply and ig destroy, ig streams real-time progress from Terraform and Pulumi. Both providers emit NDJSON events when run with --json, which ig parses to show per-resource status.
TTY (default compact view) — one line per workspace showing resource count, current operation, and elapsed time:
ok redis 2/5 resources creating docker:index:Container (3s)
ok postgres 1/3 resources creating aws:rds:Instance (15s)
TTY verbose (-v) — expanded per-resource detail:
* redis
ok docker:index:Image pulled 0.8s
* docker:index:Container creating... 3s
Non-TTY/CI — append-only prefixed lines (no ANSI cursor codes):
[redis] create docker_network.main
[redis] create docker_network.main (12s)
[postgres] error: aws_rds.main - DBInstanceAlreadyExists
Two modes are available for GitLab MR-based approval:
Bridge-less mode (ig ci) — single pipeline job, no external service:
# .gitlab-ci.yml
infraglue:
script: ig ci --env $CI_ENVIRONMENT_NAME
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
On each pipeline run, ig ci reads MR comments + emoji reactions and determines what to do:
After approving a level (thumbsup on the comment), manually re-trigger the pipeline (/run_pipeline or "Run pipeline" button in MR). Levels with no changes are auto-skipped.
Bridge mode (ig plan + @krafteq/infraglue-bridge) — webhook-driven, instant reaction:
MR push → ig plan → posts comment per level
User 👍 comment → bridge webhook → triggers pipeline
ig apply (auto: --approve all --up-to-level N) → applies → re-plans → posts new comments
ig plan auto-detects GitLab MR pipelines and posts comments. The bridge service receives emoji webhooks and instantly triggers apply pipelines via the GitLab trigger API.
Both modes use the same comment format with hidden <!-- ig-meta:{...} --> tags.
| Option | Description |
|---|---|
-d, --directory <dir> | Root directory of the monorepo (defaults to .) |
-v, --verbose | Verbose output |
-q, --quiet | Quiet output |
--strict | Fail on most warnings |
| Variable | Values | Description |
|---|---|---|
IG_DEBUG / IG_VERBOSE | 1 | Enable verbose/debug output (same as --verbose flag) |
IG_DISABLE_STATE_OUTPUTS | 1, true | Skip caching workspace outputs in .ig/state.json. Outputs are always fetched live from the provider. Use when you don't want secrets persisted to disk. |
VAULT_ADDR | URL | Vault server address (fallback when vault.address is not set in ig.yaml) |
VAULT_SERVER_URL | URL | GitLab CI alias for VAULT_ADDR (used as fallback) |
VAULT_TOKEN | string | Vault token for authentication (highest priority auth method) |
VAULT_ID_TOKEN | JWT string | JWT token for Vault JWT auth (e.g., from GitLab CI id_tokens) |
VAULT_ROLE | string | Vault role for JWT auth (fallback when vault.role is not set in ig.yaml) |
VAULT_AUTH_ROLE | string | GitLab CI alias for VAULT_ROLE (used as fallback) |
VAULT_AUTH_MOUNT | string | Auth mount path for JWT auth (defaults to jwt) |
VAULT_AUTH_PATH | string | GitLab CI alias for VAULT_AUTH_MOUNT (used as fallback) |
ig drift detects two types of drift without modifying state:
terraform plan -refresh-only / pulumi refresh --preview-only.terraform plan -refresh=false / pulumi preview (refresh disabled to isolate code-vs-state changes from cloud drift).Both checks run per workspace by default. Use --refresh-only to skip configuration drift and only check infrastructure drift.
--json outputs a DriftReport with per-workspace drift status including infrastructureDrift and configurationDrift sub-reports--refresh-only skips configuration drift check (only cloud vs state)ig import and ig export are single-workspace commands (require --project and --env). Arguments after the command are passed through to the provider:
ig import <address> <cloud-id> --project <ws> --env <env>ig import <type> <name> <cloud-id> --project <ws> --env <env>my-service/)ig.yaml workspace field.tf files for Terraform, Pulumi.yaml + index.ts for Pulumi)my-service/ig.yaml with environment config:envs:
dev:
backend_type: local
backend_config:
path: ./terraform_dev.tfstate
vars:
name: dev_my_service
injection:injection:
vpc_id: '../networking:vpc_id'
envs:
dev:
backend_type: local
backend_config:
path: ./terraform_dev.tfstate
ig config show to verify the workspace is discovered and dependencies are correctig apply --env dev to applyIf provider is not set in a workspace's ig.yaml, ig detects it automatically:
.tf filesPulumi.yamlThe selected environment is stored in .ig/state.json at the monorepo root. This file is created by ig env select and persists across commands. Pass --env to override without changing the stored selection.
When ig plan or ig apply detects no resource changes in a workspace, it compares the plan's outputs against cached state outputs. If outputs differ (new exports added, values changed, or exports removed), the workspace is included in the plan instead of being skipped. This is particularly useful for Pulumi workspaces where output-only changes (e.g., adding a new pulumi.export()) don't appear as resource changes in the preview.
| Error | Cause | Fix |
|---|---|---|
Monorepo not found in <dir> | No ig.yaml with workspace field found | Ensure root ig.yaml exists with workspace globs |
No environment selected | No env stored and no --env flag | Run ig env select <env> or pass --env |
Single workspace is required | provider command run from monorepo root | cd into a workspace dir or pass --project |
| Workspace not discovered | Directory doesn't match any workspace glob | Check glob patterns in root ig.yaml |
| Provider not detected | No .tf or Pulumi.yaml files in workspace | Add IaC files or set provider explicitly in workspace ig.yaml |
Environment variable 'X' is not set | ${X} used in config but X is not in env | Set the env var or use $${X} to escape as literal |
| Provider state locked after failed apply | A previous ig apply or ig destroy failed mid-run | Run ig provider force-unlock <lock-id> in each failed workspace, or pulumi cancel for Pulumi |
| Destroy fails with missing upstream | Upstream workspace already destroyed in a prior run | ig >=0.3.0 handles this automatically with placeholder inputs. On older versions, re-apply upstream first or use --no-deps |
| Corrupted state / lost outputs | Multiple ig processes ran concurrently | Never run ig commands in parallel — ig uses a process-local mutex on .ig/state.json. Wait for each command to finish |
Vault authentication failed: no token | No VAULT_TOKEN, ~/.vault-token, or VAULT_ID_TOKEN | Run vault login locally, or set VAULT_TOKEN / VAULT_ID_TOKEN in CI |
Vault access denied for path '...' | Token lacks permission for the secret path | Check Vault policies for the token/role |
Vault secret not found at '...' | Secret path doesn't exist or wrong KV v2 path | Ensure the path includes secret/data/ prefix (KV v2). Verify with vault kv get <path> |
no vault configuration provided | ${vault:...} used but no Vault address configured | Set VAULT_ADDR env var or add vault.address to root ig.yaml |
Vault JWT auth requires a role | JWT auth attempted without a role | Set vault.role in ig.yaml or VAULT_ROLE env var |
まだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
Add --json flag to CLI commands for machine-readable output on stdout. Generic pattern for any CLI tool, enabling piping to jq and CI/CD integration while keeping human-readable output on stderr.
日本語の概要は準備中です。原文の説明を表示しています。
Add shell completion support via a 'completion' subcommand for bash, zsh, and fish. Generic pattern for any Commander.js CLI, with static and dynamic completion examples.
日本語の概要は準備中です。原文の説明を表示しています。
Add zod schema validation for CLI options and YAML/JSON config files. Generic pattern for runtime validation with type-safe parsing and clear error messages in any Commander.js CLI.
日本語の概要は準備中です。原文の説明を表示しています。
Set up @changesets/cli for semantic versioning, CHANGELOG generation, and npm publishing in a monorepo or single package. Covers init, config, contributor workflow, GitHub Actions release automation, and npm provenance.
日本語の概要は準備中です。原文の説明を表示しています。
Add automatic CI/CD environment detection using ci-info package. Generic pattern for any CLI tool that needs to adapt behavior (prompts, output, integrations) based on whether it's running in CI.
日本語の概要は準備中です。原文の説明を表示しています。
Polish CLI user experience with @clack/prompts for interactive flows, update-notifier for version awareness, and enhanced Commander.js help text with examples and colors. Generic patterns for any Node.js CLI.
日本語の概要は準備中です。原文の説明を表示しています。