Infra CLI
Mirrored from
iblai/infra-cliยทREADME.md. This page is generated โ edit it in the repository, not here.
Interactive CLI for provisioning and configuring the ibl.ai platform on AWS or GCP (single-server). End-to-end infrastructure with Terraform, full application setup with Ansible. Can also bootstrap existing servers (any cloud or bare metal) without Terraform.
AWS GCP Terraform Python 3.11+ License: MIT
Note: This repository contains the installation and infrastructure provisioning tooling. Access to the ibl.ai Docker images and platform codebase requires a license. To get started, reach out at ibl.ai/contact.
Quick start
Five steps from zero to a running platform:
# 1. Install (needs Python 3.11+, Terraform on PATH)
git clone git@github.com:iblai/iblai-infra-ops.git && cd iblai-infra-ops
uv sync # AWS only
uv sync --extra gcp # AWS + GCP support
# 2. Verify your cloud credentials have what's needed
uv run iblai infra permissions --check # AWS
uv run iblai infra permissions --provider gcp --check --project <ID> # GCP
# 3. Provision the infrastructure (interactive wizard โ pick AWS or GCP)
uv run iblai infra provision
# 4. Install the platform on the new VM (Ansible over SSH)
uv run iblai infra setup <project-name>
# 5. Open the app
# https://learn.<your-domain>
Prefer no prompts? Steps 3โ4 also run from a .env file โ see Non-interactive.
Deploying on Google Cloud? The flow above is identical; read the short GCP guide first for GCP prerequisites (project, APIs, auth) and cert/DNS behavior.
Prerequisites
| Requirement | |
|---|---|
| Always | Python 3.11+ ยท uv (or pip) ยท Terraform on PATH ยท a domain you control ยท GitHub access to the private ibl.ai packages (licensed) |
| AWS | An account with EC2, ELB, S3, ACM, Route53, IAM, STS permissions (iblai infra permissions prints the exact policy) |
| GCP | A project with billing; compute + dns APIs enabled; roles compute.admin, dns.admin, iam.serviceAccountUser; auth via gcloud auth application-default login or a service-account key. AWS S3 is still used for object storage โ see the GCP guide |
| Existing server (bare metal / other cloud) | SSH access to the machine (Ubuntu 22.04, min 100 GB disk) and DNS you can point at it. No Terraform runs; only iblai infra setup is used. AWS credentials are still required โ see below |
AWS credentials are required on every deployment, including bare metal and GCP. They are not for hosting; they cover two things:
ECR โ pulling the platform container images.
S3 โ the platform keeps media, static assets, and backups in three buckets. Provisioning on AWS creates them for you. On any other target, create them yourself before setup, named:
<project>-<environment>-<domain-with-dots-as-dashes>-backups <project>-<environment>-<domain-with-dots-as-dashes>-dm-media <project>-<environment>-<domain-with-dots-as-dashes>-dm-static # public-read
Installed automatically as Python dependencies: ansible-core (runs the setup), boto3 (AWS SDK), and โ with --extra gcp โ the Google Cloud SDKs. Terraform is called as a subprocess and must be installed separately.
What setup installs on the target server: Docker Engine + compose, pyenv + Python 3.11.8, AWS CLI v2, and iblai-cli-ops (the platform management CLI โ private repository; unauthenticated requests see a 404).
Install
curl -LsSf https://astral.sh/uv/install.sh | sh # install uv if needed
git clone git@github.com:iblai/iblai-infra-ops.git
cd iblai-infra-ops
uv sync --extra gcp # or plain `uv sync` for AWS only
Verify:
uv run iblai --version
terraform --version
Every command below can be run as uv run iblai ..., or just iblai ... after source .venv/bin/activate.
Usage
Run iblai infra with no arguments for an interactive menu of all commands.
1. Check cloud permissions
iblai infra permissions [--check] # AWS: IAM policy JSON + dry-run
iblai infra permissions --provider gcp [--check --project <ID>] # GCP: roles + APIs + probe
2. Provision infrastructure
iblai infra provision
The wizard walks you through, validating as you go:
- Cloud โ AWS or GCP
- Credentials โ AWS: profile / keys / env vars ยท GCP: project ID, region/zone, ADC or key file
- Topology โ AWS: single-server, multi-server, or call-server (LiveKit) ยท GCP: single-server
- Project & compute โ name, environment, machine/instance type, disk (min 100 GB; a 32 GB-RAM pick prints a heads-up โ 64 GB recommended when AI features are on)
- Network & SSH โ CIDR, your IP for SSH access (auto-detected), SSH key (generate or provide)
- Domain & certificates โ AWS: Route53 + ACM / upload / none ยท GCP: Cloud DNS + Google-managed cert (validates asynchronously, 10โ60 min) / upload / none
- WAF (optional, AWS single-server) โ AWS WAFv2 on the ALB; skippable, add later with
iblai infra waf enable - Review โ full summary before anything is created
Terraform then runs with live per-resource progress. The results panel shows the VM IP, SSH command, and app URL.
3. Set up the platform
iblai infra setup <name> # a Terraform-provisioned environment (IP/domain/key auto-filled)
iblai infra setup # any existing server (bare metal / other cloud) โ prompts for everything
Prompts for: release tag of iblai-prod-images (the one version knob โ the matching iblai-cli-ops version is resolved automatically from its pin), tenant platform name (blank = default), enable-AI toggle, optional integrations (SMTP / Stripe / Google SSO / Microsoft SSO โ each off unless configured), GitHub PAT, AWS credentials (ECR + S3), OpenAI key (optional), and super admin credentials.
Everything optional can wait. SMTP, SSO, Stripe and the OpenAI key are each skippable here โ the platform comes up without them. Only the GitHub PAT, AWS credentials and admin credentials are needed on the first run. Add any of them later with iblai infra configure <name> โ no need to re-run setup.
Installing from your own repos. Three prompts control where the private packages come from. Press Enter to accept the defaults, or point them at a fork or per-deployment copy:
| Prompt | Default |
|---|---|
GitHub org owning the private packages | iblai |
CLI ops repo (or repo/subdir for monorepo) | iblai-cli-ops |
Prod images repo (or repo/subdir for monorepo) | iblai-prod-images |
Both repo prompts also accept a repo/subdir path when the two packages live in one repository. The GitHub PAT needs Contents: Read on whichever repositories you name. The same three values are available as GITHUB_ORG, CLI_OPS_REPO and PROD_IMAGES_REPO in .env.setup.example.
The playbook runs 16 roles in phases:
| Phase | Roles | What it does |
|---|---|---|
| Host setup | docker, awscli, python | Docker + compose, AWS CLI v2, pyenv + Python 3.11.8 |
| Platform install | ibl_cli_ops, ibl_platform | Installs the pinned platform packages; configures domain, gateway, defaults |
| Core services | ibl_dm, ibl_edx, ibl_spa | Data Manager (Django/Postgres/Redis/Celery), Open edX (LMS/CMS/MySQL/Mongo/ES), and the Auth/Mentor/Skills SPAs |
| Finalization | integrations, admin_setup, data_seeding, ibl_tenant_platform | OAuth/OIDC, edXโDM sync, super admin, data seeding, optional tenant launch |
| Optional | smtp_config, stripe_config, google_sso_config, microsoft_sso_config | Each no-ops unless its trigger key is set |
4. Non-interactive provision + setup (.env file)
Same Terraform + Ansible, zero prompts. Single-server only.
# Provision โ AWS
cp .env.provision.example .env && $EDITOR .env
iblai infra provision-env -f .env
# Provision โ GCP (sets PROVIDER=gcp)
cp .env.provision.gcp.example .env && $EDITOR .env
iblai infra provision-env -f .env
# Set up the provisioned VM
cp .env.setup.example .env.setup && $EDITOR .env.setup
iblai infra setup-env <project-name> -f .env.setup
Free-standing server (no Terraform): omit the project name and add TARGET_HOST, SSH_PRIVATE_KEY_PATH, BASE_DOMAIN, PROJECT_NAME to .env.setup.
Sample .env files (every key documented inline โ copy, edit, run):
| Sample | Used by |
|---|---|
.env.provision.example | provision-env โ fresh AWS single-server |
.env.provision.gcp.example | provision-env โ fresh GCP single-server |
.env.setup.example | setup-env โ platform install on a provisioned or free-standing VM |
.env.example | launch-env โ one-shot AMI launch (AWS) |
Populated .env files are gitignored; secrets are never persisted to state.json โ they ride --extra-vars into Ansible at run time only.
5. Re-setup an existing environment
iblai infra resetup <name>
Points a running environment at a new domain with fresh secrets: prompts for the new base domain, prod-images release tag, and credentials; rotates all secrets, syncs DB passwords, restarts services. No Terraform changes.
6. Verify DNS
iblai infra dns check <name> # check once
iblai infra dns check <name> --watch # re-check until everything resolves
Unless the deployment manages DNS itself (Route53 + ACM, or an existing Cloud DNS zone), you create the records โ see the list printed after provisioning, also saved as dns-records.txt in the workspace. Provisioning offers to run this check straight away, and it can be re-run at any point while you wait for the records to appear.
Per subdomain it reports whether the name resolves and whether it points at this deployment's load balancer โ a record resolving to some other host is flagged rather than passed. Certificate state is shown alongside, since an ACM or Google-managed certificate cannot validate until the records exist. Lookups go to public resolvers, so a stale local cache can't mask a failure. Exits non-zero while anything is unresolved.
iblai infra setup <name> runs the same check first and asks before installing against domains that don't resolve; --skip-dns-check bypasses it.
7. Optional feature toggles (post-provision)
Everything optional at setup can be added later against a running environment, without re-running setup:
iblai infra configure <name> # menu of everything below
The environment must already be provisioned and set up. Only the relevant Ansible role re-runs, and it needs just the SSH key already recorded in the project โ no GitHub token or AWS keys. Most changes are live immediately; the two exceptions are noted below.
Email (SMTP)
iblai infra smtp enable <name> # prompts for host, port, credentials, sender
iblai infra smtp enable-env <name> -f .env # non-interactive (SMTP_* keys)
iblai infra smtp disable <name> [--yes]
iblai infra smtp status <name> # reads the live values off the server
โ ๏ธ The only feature needing a restart โ the settings reach the services as environment variables, so Data Manager and Open edX are recreated. The command asks first; --no-restart skips it (the change stays inert until they restart), --yes skips the prompt.
Google SSO
iblai infra sso google <name>
Register an OAuth client in Google Cloud Console first, with authorised redirect URI https://learn.<your-domain>/auth/complete/google-oauth2/. Prompts for client ID, secret, and an optional Workspace domain to restrict to. Live immediately.
Microsoft SSO
iblai infra sso microsoft <name> [--yes]
Register an application in Azure first, with redirect URI https://learn.<your-domain>/auth/complete/azuread-oauth2/. Prompts for client ID, secret and tenant ID.
โ ๏ธ Restarts Open edX โ it changes settings read only at boot. The command warns first.
Stripe billing
iblai infra stripe enable <name>
iblai infra stripe enable-env <name> -f .env # non-interactive (STRIPE_* keys)
Prompts for test/live mode, secret and publishable keys, and optional pricing-table and webhook values. Live mode warns that real cards will be charged. Live immediately.
LLM API key
iblai infra llm set-key <name>
iblai infra llm set-key <name> --provider anthropic --api-key <key> # non-interactive
Sets or rotates the credential the mentor service uses. Supported providers are openai (the default) and anthropic. Setting a key also makes that provider the preferred one, since the platform picks the preferred credential with no tie-break when several are marked. Live immediately.
Tenant platform
iblai infra platform create <name>
iblai infra platform create <name> --platform-name <tenant>
Creates a platform alongside the default main one, via the platform launcher so it comes up fully wired. The tenant admin credentials are printed once and stored nowhere else โ save them.
Clone a SPA
iblai infra spa clone <name> # prompts for source, name and domain
iblai infra spa list <name> # what's deployed, with ports
iblai infra spa remove <name> --spa <clone>
Runs a copy of a deployed SPA alongside the original โ its own port, its own domain โ so it can be customised without touching the one the platform depends on. The copy starts with the source's running environment file, so it behaves identically until you change it:
# customise, then restart
/ibl/app/ibl-spa/<clone>/.env.<clone>
cd /ibl/app/ibl-spa/<clone> && docker compose up -d
Ports are allocated from 5060 (stock SPAs hold 5000โ5009). The nginx block lands in /etc/nginx/conf.d/custom_domains/, which survives ibl global-proxy regeneration. Served over HTTP โ point the domain at the server and put it behind whatever already terminates TLS. remove refuses the platform's own SPAs.
WAF (AWS single-server only)
iblai infra waf enable [<name>] # WAFv2 on the ALB
iblai infra waf enable-env [<name>] -f .env # non-interactive (WAF_ALLOWED_IPS)
iblai infra waf disable <name> [--yes]
iblai infra waf status [<name>]
The only one of these that changes infrastructure rather than platform config โ it re-applies Terraform.
8. Launch from AMI (AWS, CI/CD)
One-shot Terraform + Ansible from a pre-built AMI:
iblai infra launch-env # .env-driven (cp .env.example .env first)
iblai infra launch \ # fully flag-driven
--ami-id $AMI_ID --domain $DOMAIN --hosted-zone-id $HOSTED_ZONE_ID \
--aws-key-id $AWS_ACCESS_KEY_ID --aws-secret-key $AWS_SECRET_ACCESS_KEY \
--ssh-public-key "$SSH_PUBLIC_KEY" --ssh-key $SSH_KEY_PATH \
--git-token $GIT_TOKEN --vpn-ip $VPN_IP \
--admin-email $ADMIN_EMAIL --admin-password $ADMIN_PASSWORD
See iblai infra launch --help for all options (instance type, --platform-name, SMTP/Stripe/SSO, --enable-ai, ...).
9. Service update (image updates, CI/CD)
Update container images and restart services โ no provisioning, no secret rotation:
iblai infra service-update --host <ip> --ssh-key ~/.ssh/key.pem --git-token $GIT_TOKEN
Or launch a fresh AMI and swap it into an ALB target group โ see iblai infra service-update --help.
10. Manage environments
iblai infra list # all environments (cloud, type, status)
iblai infra status <name> # details + outputs for one
iblai infra auth # switch AWS credentials
iblai infra destroy <name> # tear everything down
Authentication
The CLI never silently auto-detects credentials โ you always choose, interactively on first use, and the session is reused across commands.
AWS โ pick one in the wizard: profiles from ~/.aws/ (type to filter), environment variables, or manual access keys.
GCP โ two options; the wizard asks which:
Option A โ your Google account (local use):
gcloud auth application-default login # note: `gcloud auth login` alone is NOT enough
Option B โ service-account key (CI, or if your org blocks the browser flow above):
PROJECT=<your-project-id>
gcloud iam service-accounts create infra-cli --project=$PROJECT
for R in roles/compute.admin roles/dns.admin roles/iam.serviceAccountUser; do
gcloud projects add-iam-policy-binding $PROJECT \
--member="serviceAccount:infra-cli@$PROJECT.iam.gserviceaccount.com" --role=$R --condition=None
done
gcloud iam service-accounts keys create ~/infra-cli-key.json \
--iam-account="infra-cli@$PROJECT.iam.gserviceaccount.com"
Then choose "Service-account key file" in the wizard (or set GCP_CREDENTIALS_FILE=~/infra-cli-key.json in your .env). Keep the key file out of git; delete the service account when done.
What gets created
AWS (Terraform): VPC with 2 public subnets ยท EC2 (Ubuntu 22.04, encrypted EBS) ยท Application Load Balancer (TLS 1.2/1.3) ยท ACM certificates (DNS-validated, auto-renewed) ยท security groups (SSH restricted to your IP) ยท 3 S3 buckets (backups, media, static) ยท Route53 A-records for all subdomains ยท optional WAFv2.
GCP (Terraform): VPC + regional subnet ยท Compute Engine VM (Ubuntu 22.04) ยท global external Application Load Balancer with a static IP ยท Google-managed SSL certificate (all subdomains, async validation) ยท firewall rules (SSH restricted to your IP, health-check ranges) ยท Cloud DNS A-records. Object storage stays on AWS S3 โ details in the GCP guide.
Subdomains โ each gets a DNS A-record and is covered by the load balancer's TLS certificate. Replace <your-domain> with your base domain:
learn.<your-domain>preview.learn.<your-domain>studio.learn.<your-domain>apps.learn.<your-domain>meilisearch.learn.<your-domain>api.<your-domain>api.data.<your-domain>asgi.data.<your-domain>llm.data.<your-domain>base.manager.<your-domain>auth.<your-domain>os.<your-domain>lms.<your-domain>platform.<your-domain>monitor.<your-domain>flowise.<your-domain>prometheus.<your-domain>
Platform services (Ansible): Open edX (LMS/CMS + MySQL/Redis/MongoDB/Elasticsearch/Forum) ยท Data Manager (Django/ASGI/Celery + PostgreSQL 16/Redis/Flowise) ยท Auth/Mentor/Skills SPAs ยท monitoring (Prometheus/Grafana) ยท nginx reverse proxy.
Workspace
Terraform state, generated SSH keys, and project configuration live at ~/.iblai-infra/projects/<project-name>/.
Development
uv sync --extra dev --extra gcp
uv run pytest tests/ -v # 749 tests, ~2s
uv run pytest tests/ --cov=iblai_infra --cov-report=term-missing
iblai-infra-ops/
โโโ src/iblai_infra/
โ โโโ cli.py # Typer CLI commands
โ โโโ app.py # Provision wizard orchestrator
โ โโโ models.py # Pydantic models (the wizard โ Terraform contract)
โ โโโ env_provision.py # .env โ InfraConfig (provision-env, AWS)
โ โโโ gcp_env_provision.py # .env โ InfraConfig (provision-env, GCP)
โ โโโ env_setup.py # .env โ SetupConfig (setup-env)
โ โโโ prompts/ # Interactive questionary prompts
โ โโโ providers/ # aws.py + gcp.py โ cloud SDK helpers
โ โโโ features/ # Post-provision toggles (waf.py)
โ โโโ terraform/ # Runner + templates
โ โ โโโ templates/
โ โ โโโ aws/ # single-server (+ waf.tf), multi-server, call-server
โ โ โโโ gcp/ # single-server
โ โโโ ansible/ # Runner + playbooks + 16 roles
โโโ docs/ # GCP guide, development notes
โโโ tests/ # 749 tests
โโโ pyproject.toml
License
Released under the MIT License.