Metadata-Version: 2.4
Name: infra-cli
Version: 0.1.4
Summary: Pluggable Infrastructure CLI as a wrapper around DevOps tools.
Author-email: Youqing Han <ihanyouqing@gmail.com>
License-Expression: Apache-2.0
Project-URL: Homepage, https://github.com/hanyouqing/infra-cli
Project-URL: Repository, https://github.com/hanyouqing/infra-cli
Project-URL: Bug Tracker, https://github.com/hanyouqing/infra-cli/issues
Project-URL: Changelog, https://github.com/hanyouqing/infra-cli/blob/main/CHANGELOG.md
Project-URL: Author, https://github.com/hanyouqing
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Classifier: Topic :: Software Development :: Build Tools
Classifier: Topic :: System :: Systems Administration
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: typer>=0.9.0
Requires-Dist: typing-extensions>=4.0.0
Requires-Dist: PyYAML>=6.0
Requires-Dist: boto3>=1.20.0
Requires-Dist: rich>=10.0.0
Requires-Dist: questionary>=2.0.0
Requires-Dist: requests>=2.28.0
Provides-Extra: aws
Requires-Dist: zxcvbn>=4.4.28; extra == "aws"
Requires-Dist: openpyxl>=3.1.0; extra == "aws"
Provides-Extra: server
Requires-Dist: fastapi>=0.100.0; extra == "server"
Requires-Dist: uvicorn>=0.20.0; extra == "server"
Requires-Dist: markdown>=3.4.0; extra == "server"
Provides-Extra: test
Requires-Dist: pytest>=7.0.0; extra == "test"
Requires-Dist: pytest-mock>=3.6.1; extra == "test"
Requires-Dist: pytest-cov>=4.0.0; extra == "test"
Requires-Dist: httpx>=0.27.0; extra == "test"
Provides-Extra: dev
Requires-Dist: ruff>=0.1.0; extra == "dev"
Requires-Dist: mypy>=1.0.0; extra == "dev"
Requires-Dist: types-PyYAML; extra == "dev"
Requires-Dist: types-setuptools; extra == "dev"
Requires-Dist: boto3-stubs[essential]; extra == "dev"
Requires-Dist: ansible-core>=2.16.0; extra == "dev"
Requires-Dist: ansible-lint>=24.0.0; extra == "dev"
Requires-Dist: build>=1.0.0; extra == "dev"
Requires-Dist: twine>=5.0.0; extra == "dev"
Dynamic: license-file

# infra-cli

[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![Python Version](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)

`infra-cli` is a pluggable infrastructure management tool designed to wrap common DevOps tools (Terragrunt, Terraform, Ansible, Packer, etc.) into a single, unified interface. It simplifies complex workflows through sensible conventions and centralized configuration.

## 🚀 Key Features

- **Unified Interface**: A single command (`infra`) to manage multiple infrastructure tools.
- **Plugin Architecture**: Easily extensible logic for different tools.
- **Convention over Configuration**: Default directory structures that "just work."
- **Centralized Config**: Manage all tool settings in one place (`~/.infra/config.yaml`).
- **AWS SSO Integration**: Seamlessly handles authentication and profile generation.
- **Local Environment Setup**: Automate the configuration of your macOS or Ubuntu dev environment.

## 🛠 Supported Plugins

- **`setup`**: Bootstrap your local development environment (macOS/Ubuntu).
- **`ansible`**: Wrapper for `ansible-playbook` with automatic project scaffolding.
- **`aws`**: SSO login, landing zones, and cross-account commands (`infra aws <service> <verb>`), including EC2 / RDS / ElastiCache inventory and SSM password audits.
- **`packer`**: Build and validate machine images consistently.
- **`terraform`** / **`tf`**: Run Terraform under `$INFRA_REPO/terraform/sites/<env>/<stack>`; `scaffold` creates `terraform/{sites,modules}`.
- **`terragrunt`** / **`tg`**: Run Terragrunt under `$INFRA_REPO/terragrunt/<env>/<account>/<region>/<stack>` (sibling of `terraform/`).
- **`github`**: Manage repository settings and scaffolding.
- **`slack`**: Send notifications to Slack webhooks.
- **`server`**: Local, read-only HTML command reference (`infra server start`).

## 🏁 Quick Start

### Installation

Install with [pipx](https://pypa.github.io/pipx/) from **PyPI** or a **git checkout**. Full bootstrap for fresh vs existing machines: **[INSTALL.md](INSTALL.md)**.

| Source | Command |
| --- | --- |
| PyPI | `pipx install infra-cli` |
| Checkout | `git clone … && cd infra-cli && pipx install .` |

Fresh Mac needs [Xcode CLT + pipx first](INSTALL.md#fresh-macos). Existing dotfiles and brew/apt packages are preserved by `infra setup`.

```bash
pipx install infra-cli              # or: pipx install .
pipx inject infra-cli '.[aws]'      # optional extras
infra doctor
infra setup doctor
```

**Upgrade infra-cli:** `infra setup upgrade-cli --pypi` · from checkout: `--from-path ~/…/infra-cli` · after setup run: `infra setup run --upgrade-cli --pypi`

### Basic Usage

```bash
# Full workstation bootstrap (all playbook tags)
infra setup

# Layered install (default profile for `run`: core)
infra setup run
infra setup run --profile cloud
infra setup doctor
infra setup list

# Targeted component install (same playbook, fixed tags)
infra setup packer
infra setup aliyun      # also: infra setup alicloud
infra setup aws
infra setup terraform
infra setup trivy
infra setup k8s

# Login to AWS SSO (picks a landing zone when several are configured)
infra aws login

# Re-login before a long terraform/terragrunt run to refresh the SSO token
infra aws login --lz prod

# Log out of that org's SSO session and expire cached role credentials
infra aws logout
infra aws logout --lz prod
infra aws logout --all

# Pick a profile for the current shell
eval "$(infra aws select)"

# Alibaba Cloud CloudSSO (requires Alibaba Cloud CLI 3.0.271+)
# infra aliyun  = China Website (aliyun.com)  · ALIBABA_CLOUD_ACCOUNT_TYPE=Domestic
# infra alicloud = International Website (alibabacloud.com) · International
# Accounts are not interchangeable — use the matching subcommand.
infra aliyun landing-zones add prod \
  https://signin-******.alibabacloudsso.com/device/login cn-hangzhou
infra aliyun login --lz prod
infra alicloud landing-zones add prod \
  https://signin-******.alibabacloudsso.com/device/login ap-southeast-1
infra alicloud login --lz prod
eval "$(infra aliyun select)"
eval "$(infra alicloud select)"
infra aliyun profiles
infra alicloud profiles

# Inventory EC2 / RDS / ElastiCache across SSO accounts
infra aws ec2 list
infra aws rds list --excel
infra aws elasticache list --excel

# Inspector HIGH/CRITICAL findings for ECR images
infra aws ecr issues

# Audit SSM SecureString strength (skips prod by default)
infra aws ssm audit --profile-filter '-admin$'

# Run an Ansible playbook
infra ansible playbook site.yml

# Terraform / Terragrunt share one INFRA_REPO, split at the top level:
#   $INFRA_REPO/terraform/…   $INFRA_REPO/terragrunt/…
# Omit the verb → plan; -y with no verb → apply -auto-approve. Auto-init when needed.
infra terraform scaffold ~/github.com/acme/infra
infra terragrunt scaffold -e dev -a personal -r us-east-1 -s vcn ~/github.com/acme/infra
export INFRA_REPO=~/github.com/acme/infra
infra terraform -e stg -s platform/example
infra tg -e prod -s workload/us-east-1/vpc -ss network/us-east-1/tgw plan
infra tf -e prod -s platform/devops -t module.vpc
infra terraform -y -e stg -s platform/example
infra tf apply -e prod -s platform/devops -y
# After destroy: cleans .terraform locally and prints `rm -rf` for the stack; --rm deletes it
infra tf destroy -e stg -s platform/example -y
infra tf destroy -e stg -s platform/example -y --rm

# Check configuration
infra config list

# Local HTML cheatsheet of every command (needs: pip install 'infra-cli[server]')
infra server start
infra server start --no-browser -p 9000
```

Every command accepts `-h` / `--help` (GNU long options; short flags where they do not clash). `infra` with no arguments prints the same overview as `infra -h`. Nested groups also print help when invoked with no verb (`infra server`, `infra aws`, `infra config`, …).

```bash
infra -h
infra aws login -h
infra aliyun login -h
infra alicloud login -h
infra server -h
infra server start -h
```

## Naming recommendations (optional)

`infra-cli` suggests — but never enforces — a consistent naming pattern for cloud accounts and CLI profiles. Existing account aliases and `~/.aws/config` profile names keep working unchanged.

| Kind | Pattern | Example |
|------|---------|---------|
| Cloud account alias | `<workload>-<env>` | `payments-prod` |
| CLI profile | `<workload>-<role>-<env>` | `payments-admin-prod` |
| Role segment | `admin`, `readonly`, `deploy`, or team name | `payments-deploy-stg` |

**Why env as suffix:** the last `-` segment is easy to extract programmatically and aligns with canonical env tiers (`prod`, `stg`, `test`, `dev`, `sandbox`).

**Directory layout vs cloud alias:** Terragrunt on-disk account dirs stay short (`terragrunt/dev/personal/...`); the cloud account alias may add the env suffix (`personal-dev`). Terraform sites put env in the path (`terraform/sites/dev/platform`) — stack names should be workload-only, not `platform-dev`.

`infra tg scaffold` and `infra tf scaffold` print examples after a successful run. See also `src/infra/templates/aws_sso/config.ini`.

## ⚙️ Configuration

`infra-cli` uses a YAML configuration file located at `~/.infra/config.yaml`.

### Managing Config via CLI

You can get or set configuration values directly:

```bash
# Set a default user name for the setup plugin
infra config set setup.user_name=your_name

# View current configuration
infra config list
```

### Manual Configuration

The default `config.yaml` is generated on first run with sensible defaults. You can customize per-plugin settings like `region`, `env`, or `extra_args`.

Optional AWS extras (Excel export + zxcvbn scoring):

```bash
pipx inject infra-cli '.[aws]'
# or, from a checkout during install:
pipx install '.[aws]'
```

## AWS inventory and audit

Commands follow the AWS CLI layout (`aws <service> <verb>`), but each verb can scan every profile in `~/.aws/config` (typically generated by `infra aws login`). Accounts without credentials are skipped instead of aborting the whole run.

```bash
infra aws --help
infra aws login --help
infra aws logout --help
infra aws profiles --help
infra aws ec2 list --help
infra aws rds list --help
infra aws elasticache list --help
infra aws ssm audit --help
infra aws ssm search --help
infra aws ecr login
infra aws ecr issues
infra aws ecr issues --format json --filter '*base*'
```

### ECR image findings

Lists HIGH/CRITICAL Inspector findings for ECR container images in the selected account (`infra aws ecr issues`). Prefer this over a separate top-level scan command.

```bash
infra aws ecr issues
infra aws ecr issues --profile my-org-admin -r us-east-1 --format json
```

To add another service, drop a module in `src/infra/plugins/aws/services/` that exports `SERVICE` (the AWS CLI service name) and `app` (a Typer group). It is picked up automatically. Runtime extras can also call `register_service(name, app)`.

## Alibaba Cloud CloudSSO

Same `aliyun` CLI binary and CloudSSO configure flow for both sites; **accounts, billing, and some product endpoints are not interchangeable**. Implementation is shared under `src/infra/plugins/alibaba/` (`create_site_app(site)`); only the public verbs and site metadata differ.

| Subcommand | Site | `ALIBABA_CLOUD_ACCOUNT_TYPE` | Docs |
|------------|------|------------------------------|------|
| `infra aliyun` | China Website (aliyun.com) | `Domestic` | [help.aliyun.com](https://help.aliyun.com/zh/cloudsso/user-guide/use-alibaba-cloud-cli-to-access-cloudsso-and-alibaba-cloud-resources) |
| `infra alicloud` | International Website (alibabacloud.com) | `International` | [alibabacloud.com](https://www.alibabacloud.com/help/en/cloudsso/user-guide/use-alibaba-cloud-cli-to-access-cloudsso-and-alibaba-cloud-resources) |

Landing-zone caches are separate (`~/.config/infra/aliyun/` vs `~/.config/infra/alicloud/`). Sign-in URLs still use `*.alibabacloudsso.com` (including accelerated URLs). One CLI profile maps to one RD account + one access configuration.

```bash
infra aliyun --help
infra alicloud --help
infra aliyun login --help
infra alicloud login --help
eval "$(infra aliyun select)"   # also exports ALIBABA_CLOUD_ACCOUNT_TYPE=Domestic
eval "$(infra alicloud select)" # International
```

### EC2 inventory

Lists instances across accounts (`aws ec2 describe-instances`, cross-account). Terminated instances are omitted unless you pass `--include-terminated`.

```bash
infra aws ec2 list
infra aws ec2 list --profile my-org-admin --region us-east-1 --excel --json ec2.json
infra aws ec2 list --profile-filter '-admin$' --include-terminated
```

### RDS inventory

Lists DB instances across accounts (`aws rds describe-db-instances`, cross-account). Aurora members show up as instances; `cluster_identifier` links them to a cluster.

```bash
infra aws rds list
infra aws rds list --profile my-org-admin --region us-east-1 --excel --csv ./out/rds.csv
```

### ElastiCache inventory

Lists Redis/Valkey replication groups across accounts (`aws elasticache describe-replication-groups`, cross-account). One SSO profile is used per account (names containing `admin` are preferred). Region comes from the profile unless `--region` is set.

```bash
# All SSO accounts, CSV in the current directory
infra aws elasticache list

# Single profile / region, plus Excel and JSON
infra aws elasticache list --profile my-org-admin --region us-east-1 --excel --json redis.json

# Admin profiles only, including standalone cache clusters
infra aws elasticache list --profile-filter '-admin$' --include-stale-cache --csv ./out/redis.csv
```

CSV extra columns `application`, `owner_team`, and `notes` are left empty for teams to fill in. Override with `--extra-columns` or pass an empty string to disable them.

### SSM password audit

Scores SSM `SecureString` parameters with zxcvbn (`aws ssm get-parameter --with-decryption`, cross-account). Plaintext is never written to disk; ARNs, account IDs, and password-like tag keys are redacted in the CSV. Parameters tagged (or named) as `prod` are skipped unless you clear `--skip-envs`.

```bash
infra aws ssm audit
infra aws ssm audit --only-weak --out /tmp/ssm.csv
infra aws ssm audit --skip-envs '' --region us-east-1,us-west-2
infra aws ssm audit --account-names ./account-names.json
```

`--account-names` is an optional JSON object of `{"123456789012": "shared-services"}`. Without it, IAM account aliases are used when the caller can list them.

### SSM name search

Look up Parameter Store names in **one** account (`ssm describe-parameters`). Values are not read or decrypted. Pass `--profile` for the target account (or reuse the last `infra aws select`). Exit status is 0 if any name matches, 1 if none do.

```bash
infra aws ssm search redis --profile my-org-admin
infra aws ssm search /stg/app --profile my-org-admin -r us-east-1
infra aws ssm search my-param --exact --format json
infra aws ssm search password --type SecureString --profile my-org-admin
```

## 👩‍💻 Development

We welcome contributions! To set up your local development environment:

1. **Clone the repo**: `git clone https://github.com/hanyouqing/infra-cli.git`
2. **Install dev dependencies**: `make dev-install`
3. **Run linting**: `make lint`
4. **Run tests**: `make test`

For more details, see [CONTRIBUTING.md](CONTRIBUTING.md).

## 👤 Author & Contact

**Youqing Han** — maintainer of `infra-cli`.

| | |
| --- | --- |
| GitHub | [github.com/hanyouqing](https://github.com/hanyouqing) |
| Repository | [github.com/hanyouqing/infra-cli](https://github.com/hanyouqing/infra-cli) |
| Issues | [Open an issue](https://github.com/hanyouqing/infra-cli/issues) |
| Email | [ihanyouqing@gmail.com](mailto:ihanyouqing@gmail.com) |

Use Issues for bugs and feature requests. Email is fine for private contact,
Code of Conduct reports, or partnership / speaking inquiries.

## 📜 License

This project is licensed under the Apache-2.0 License. See the [LICENSE](LICENSE) file for details.

Copyright © 2026 Youqing Han.
