Metadata-Version: 2.4
Name: gcp-billing-radar
Version: 0.1.1
Summary: Scan GCP billing accounts and bulk re-link their projects to a new account atomically, with dry-run, verification, CSV results, and an auto-generated rollback plan.
Project-URL: Homepage, https://github.com/radars/gcp-billing-radar
Author: Mor Michaeli
License: MIT
License-File: LICENSE
Keywords: billing,finops,gcp,google-cloud,migration,radar,reseller
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: System :: Systems Administration
Requires-Python: >=3.9
Requires-Dist: google-cloud-billing>=1.12.0
Requires-Dist: rich>=13.0.0
Description-Content-Type: text/markdown

# gcp-billing-radar

Atomically re-link GCP projects to a new billing account **in bulk** — with dry-run by default, per-project verification, CSV results, and an auto-generated rollback plan.

Each re-link is a **single atomic API call** (`projects.updateBillingInfo`): the project moves from the old billing account to the new one with no unbilled gap and no downtime. Mid-cycle usage is split between the two accounts by Google automatically.

## Install

```bash
pip install gcp-billing-radar
```

## Auth

Uses Application Default Credentials:

```bash
gcloud auth application-default login
# or GOOGLE_APPLICATION_CREDENTIALS=/path/to/sa-key.json
```

Required permissions:

- `resourcemanager.projects.updateBillingInfo` on each project (Project Owner / Billing Project Manager)
- `billing.resourceAssociations.create` on the **target** billing account (Billing Account User or Admin)

## Usage

```bash
# 1. See what's on the old account
gcp-billing-radar list-projects -b OLD-ACCOUNT-ID --plain > projects.txt

# 2. Dry run (default — nothing is changed)
gcp-billing-radar swap -f projects.txt -b XXXXXX-XXXXXX-XXXXXX

# 3. Execute
gcp-billing-radar swap -f projects.txt -b XXXXXX-XXXXXX-XXXXXX --execute

# 4. Undo everything if needed
gcp-billing-radar rollback rollback-20260710-101500.csv
```

Projects can also be passed positionally or piped via stdin:

```bash
gcp-billing-radar list-projects -b OLD-ID --plain | \
  gcp-billing-radar swap -b NEW-ID --execute
```

## Behavior

- **Dry-run by default** — mutation requires an explicit `--execute`.
- **Idempotent** — projects already on the target account are skipped; safe to re-run.
- **Per-project isolation** — one failure (e.g. a 403) doesn't abort the batch.
- **Verification** — after each link the project's billing info is re-read and compared.
- **Rollback plan** — every successful swap is recorded with its *original* account in `rollback-<ts>.csv`, consumable by the `rollback` subcommand.
- **Results CSV** — full audit trail (`results-<ts>.csv`) for every run, including dry runs.
- Exit code is non-zero if any project failed — CI/pipeline friendly.

## Output files

Every run writes **two CSV files** to the working directory:

`results-<ts>.csv` — full audit trail of every project in the run:

```csv
project_id,old_account,new_account,status,detail
project1,OLD111-AAAAAA-BBBBBB,XXXXXX-XXXXXX-XXXXXX,success,
project2,OLD111-AAAAAA-BBBBBB,XXXXXX-XXXXXX-XXXXXX,success,
already-done,XXXXXX-XXXXXX-XXXXXX,XXXXXX-XXXXXX-XXXXXX,skip-already-linked,
```

`rollback-<ts>.csv` — the undo plan, mapping each moved project back to its original account:

```csv
project_id,restore_to_account
project1,OLD111-AAAAAA-BBBBBB
project2,OLD111-AAAAAA-BBBBBB
```

Feed the rollback file straight back in to revert: `gcp-billing-radar rollback rollback-<ts>.csv`.

## Python API

```python
from gcp_billing_radar import BillingSwapper

s = BillingSwapper()
report = s.swap_batch(["proj-a", "proj-b"], "XXXXXX-XXXXXX-XXXXXX", execute=True)
print(len(report.success), "moved")
report.write_rollback()
```

## License

MIT
