diff --git a/README.md b/README.md
index 6e184a5a..edacb9f7 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# claws
-A terminal UI for AWS resource management 👮
+A terminal UI for AWS resource management
[](https://github.com/clawscli/claws/actions/workflows/ci.yml)
[](https://github.com/clawscli/claws/releases/latest)
@@ -8,32 +8,28 @@ A terminal UI for AWS resource management 👮
[](https://go.dev/)
[](LICENSE)
-
-
-## Supported Platforms
-
-| OS | Architecture |
-|-----|-------------|
-| macOS | Intel, Apple Silicon |
-| Linux | x86_64, ARM64 |
-| Windows | x86_64 |
+
## Features
- **Interactive TUI** - Navigate AWS resources with vim-style keybindings
-- **Mouse support** - Click, scroll, hover for navigation
-- **Multi-service support** - EC2, S3, IAM, RDS, Lambda, ECS, and 65+ more services (163 resources total)
+- **69 services, 163 resources** - EC2, S3, Lambda, RDS, ECS, and more
+- **Multi-profile & Multi-region** - Query multiple accounts/regions in parallel
- **Resource actions** - Start/stop instances, delete resources, tail logs
-- **Cross-resource navigation** - Jump from VPC to subnets, from Lambda to CloudWatch Logs
-- **Profile & region switching** - Switch AWS profiles (`P`) and regions (`R`) on the fly
-- **Multi-profile selection** - Select multiple profiles with `P`, parallel fetch across accounts
-- **Multi-region selection** - Select multiple regions with `R`, parallel fetch with aggregated results
-- **Command mode** - Quick navigation with `:ec2/instances` syntax
-- **Tag search** - Browse all tagged resources across regions with `:tags` command
-- **Filtering** - Fuzzy search with `/`, tag filtering with `:tag Env=prod`
-- **Column sorting** - Sort by any column with `:sort
` command
-- **Resource comparison** - Side-by-side diff view with `m` to mark, `d` to compare
-- **Pagination** - Handle large datasets with `N` key for next page
+- **Cross-resource navigation** - Jump from VPC to subnets, Lambda to CloudWatch
+- **Filtering & sorting** - Fuzzy search, tag filtering, column sorting
+- **Resource comparison** - Side-by-side diff view
+- **6 color themes** - dark, light, nord, dracula, gruvbox, catppuccin
+
+## Screenshots
+
+| Resource Browser | Detail View | Actions Menu |
+|------------------|-------------|--------------|
+|  |  |  |
+
+### Multi-Region & Multi-Account
+
+
## Installation
@@ -50,49 +46,9 @@ brew install --cask claws
curl -fsSL https://raw.githubusercontent.com/clawscli/claws/main/install.sh | sh
```
-Options:
-```bash
-# Install specific version
-curl -fsSL https://raw.githubusercontent.com/clawscli/claws/main/install.sh | VERSION=v0.1.6 sh
-
-# Install to custom directory
-curl -fsSL https://raw.githubusercontent.com/clawscli/claws/main/install.sh | INSTALL_DIR=/usr/local/bin sh
-```
-
-> **Security Note:** Piping scripts directly to `sh` is convenient but bypasses inspection.
-> For increased security, download and review the script first:
-> ```bash
-> curl -fsSL https://raw.githubusercontent.com/clawscli/claws/main/install.sh -o install.sh
-> less install.sh # Review the script
-> sh install.sh
-> ```
-
### Download Binary
-Download from [GitHub Releases](https://github.com/clawscli/claws/releases/latest):
-
-```bash
-# macOS (Apple Silicon)
-curl -Lo claws.tar.gz https://github.com/clawscli/claws/releases/latest/download/claws-darwin-arm64.tar.gz
-tar xzf claws.tar.gz && mv claws /usr/local/bin/
-
-# macOS (Intel)
-curl -Lo claws.tar.gz https://github.com/clawscli/claws/releases/latest/download/claws-darwin-amd64.tar.gz
-tar xzf claws.tar.gz && mv claws /usr/local/bin/
-
-# Linux (x86_64)
-curl -Lo claws.tar.gz https://github.com/clawscli/claws/releases/latest/download/claws-linux-amd64.tar.gz
-tar xzf claws.tar.gz && sudo mv claws /usr/local/bin/
-
-# Linux (ARM64)
-curl -Lo claws.tar.gz https://github.com/clawscli/claws/releases/latest/download/claws-linux-arm64.tar.gz
-tar xzf claws.tar.gz && sudo mv claws /usr/local/bin/
-
-# Windows (PowerShell)
-Invoke-WebRequest -Uri https://github.com/clawscli/claws/releases/latest/download/claws-windows-amd64.zip -OutFile claws.zip
-Expand-Archive claws.zip -DestinationPath .
-# Add to PATH or move to desired location
-```
+Download from [GitHub Releases](https://github.com/clawscli/claws/releases/latest).
### Go Install
@@ -100,45 +56,24 @@ Expand-Archive claws.zip -DestinationPath .
go install github.com/clawscli/claws/cmd/claws@latest
```
-### From Source
-
-```bash
-git clone https://github.com/clawscli/claws.git
-cd claws
-go build -o claws ./cmd/claws
-```
-
## Quick Start
```bash
# Run claws (uses default AWS credentials)
claws
-# Or with specific profile
+# With specific profile
claws -p myprofile
-# Or with specific region
+# With specific region
claws -r us-west-2
-# Use environment credentials only (ignore ~/.aws config)
-# Useful for EC2 instance profiles, ECS task roles, Lambda, etc.
-claws -e
-
-# Read-only mode (disables destructive actions)
-claws --read-only
-# or
-CLAWS_READ_ONLY=1 claws
-
-# Enable debug logging to file
-claws -l debug.log
-
# Start directly on a service
claws -s ec2 # EC2 instances
claws -s rds/snapshots # RDS snapshots
-claws -s cfn # CloudFormation (alias)
-# Open detail view for specific resource
-claws -s ec2 -i i-1234567890abcdef0
+# Read-only mode (disables destructive actions)
+claws --read-only
```
## Key Bindings
@@ -146,339 +81,34 @@ claws -s ec2 -i i-1234567890abcdef0
| Key | Action |
|-----|--------|
| `j` / `k` | Navigate up/down |
-| `h` / `l` | Navigate within category (service list) |
| `Enter` / `d` | View resource details |
| `:` | Command mode (e.g., `:ec2/instances`) |
-| `:` + `Enter` | Go to dashboard (home) |
-| `~` | Go to dashboard (from service browser) |
-| `:services` | Go to service browser |
-| `:sort ` | Sort by column (ascending) |
-| `:sort desc ` | Sort by column (descending) |
-| `:tag ` | Filter by tag (e.g., `:tag Env=prod`) |
-| `:tags` | Browse all tagged resources |
| `/` | Filter mode (fuzzy search) |
-| `Tab` | Next resource type |
-| `1-9` | Switch to resource type by number |
| `a` | Open actions menu |
-| `m` | Mark resource for comparison |
-| `d` | Describe (or diff if marked) |
-| `c` | Clear filter and mark |
-| `N` | Load next page (pagination) |
-| `M` | Toggle inline metrics (EC2, RDS, Lambda) |
-| `y` | Copy resource ID to clipboard |
-| `Y` | Copy resource ARN to clipboard |
-| `Ctrl+r` | Refresh (including metrics) |
-| `R` | Select AWS region(s) (multi-select supported) |
-| `P` | Select AWS profile(s) (multi-select supported) |
+| `R` | Select region(s) |
+| `P` | Select profile(s) |
| `?` | Show help |
-| `Esc` | Go back |
-| `q` / `Ctrl+c` | Quit |
-
-### Mouse Support
+| `q` | Quit |
-| Action | Effect |
-|--------|--------|
-| Hover | Highlight item under cursor |
-| Click | Select item / navigate |
-| Scroll wheel | Scroll through lists |
-| Click on tabs | Switch resource type |
-| Back button | Navigate back (same as Esc) |
+See [docs/keybindings.md](docs/keybindings.md) for complete reference.
-### Navigation Shortcuts (Context-dependent)
+## Documentation
-| Key | Action |
-|-----|--------|
-| `v` | View VPC / Versions |
-| `s` | View Subnets / Streams / Stages |
-| `g` | View Security Groups |
-| `r` | View Route Tables / Roles / Resources |
-| `e` | View Events / Executions / Endpoints |
-| `l` | View CloudWatch Logs |
-| `o` | View Outputs / Operations |
-| `i` | View Images / Indexes |
-
-### Region Selector (`R` key)
-
-| Key | Action |
-|-----|--------|
-| `j` / `k` | Navigate up/down |
-| `Space` | Toggle region selection |
-| `a` | Select all regions |
-| `n` | Deselect all regions |
-| `/` | Filter regions |
-| `Enter` | Apply selection |
-| `Esc` | Cancel |
-
-Selected regions are queried in parallel; resources display with Region column.
-
-### Profile Selector (`P` key)
-
-| Key | Action |
-|-----|--------|
-| `j` / `k` | Navigate up/down |
-| `Space` | Toggle profile selection |
-| `l` | SSO login for selected profile |
-| `L` | Console login for selected profile (`:login`) |
-| `/` | Filter profiles |
-| `Enter` | Apply selection |
-| `Esc` | Cancel |
-
-Selected profiles are queried in parallel; resources display with Profile and Account columns.
-
-### Commands
-
-| Command | Action |
-|---------|--------|
-| `:q` / `:quit` | Quit |
-| `:login [name]` | AWS console login (default: `claws-login` profile) |
-| `:ec2/instances` | Navigate to EC2 instances |
-| `:sort ` | Sort by column |
-| `:tag ` | Filter by tag |
-| `:diff ` | Compare current row with named resource |
-| `:diff ` | Compare two named resources |
-| `:theme ` | Change color theme (saved if persistence enabled) |
-| `:autosave on/off` | Enable/disable config autosave (always saved) |
-
-**Login Details:**
-- `:login` runs `aws login --remote` using `claws-login` profile
-- `:login myprofile` uses the specified profile name instead
-- For SSO profiles, use `P` to open profile selector, then `l` for SSO login
-
-## Supported Services (69 services, 163 resources)
-
-### Compute
-| Service | Resources |
-|---------|-----------|
-| EC2 | Instances, Volumes, Security Groups, Elastic IPs, Key Pairs, AMIs, Snapshots, Launch Templates, Capacity Reservations |
-| Lambda | Functions |
-| ECS | Clusters, Services, Tasks |
-| Auto Scaling | Groups, Activities |
-| App Runner | Services, Operations |
-| Batch | Job Queues, Compute Environments, Jobs, Job Definitions |
-| EMR | Clusters, Steps |
-
-### Storage & Database
-| Service | Resources |
-|---------|-----------|
-| S3 | Buckets |
-| S3 Vectors | Buckets, Indexes |
-| DynamoDB | Tables |
-| RDS | Instances, Snapshots |
-| Redshift | Clusters, Snapshots |
-| ElastiCache | Clusters |
-| OpenSearch | Domains |
-
-### Data & Analytics
-| Service | Resources |
-|---------|-----------|
-| Glue | Databases, Tables, Crawlers, Jobs, Job Runs |
-| Athena | Workgroups, Query Executions |
-| Transcribe | Jobs |
-
-### Containers & ML
-| Service | Resources |
-|---------|-----------|
-| ECR | Repositories, Images |
-| Bedrock | Foundation Models, Guardrails, Inference Profiles |
-| Bedrock Agent | Agents, Knowledge Bases, Data Sources, Prompts, Flows |
-| Bedrock AgentCore | Runtimes, Endpoints, Versions |
-| SageMaker | Endpoints, Notebooks, Training Jobs, Models |
-
-### Networking
-| Service | Resources |
-|---------|-----------|
-| VPC | VPCs, Subnets, Route Tables, Internet Gateways, NAT Gateways, VPC Endpoints, Transit Gateways, TGW Attachments |
-| Route 53 | Hosted Zones, Record Sets |
-| API Gateway | REST APIs, HTTP APIs, Stages |
-| AppSync | GraphQL APIs, Data Sources |
-| ELB | Load Balancers, Target Groups, Targets |
-| CloudFront | Distributions |
-| Direct Connect | Connections, Virtual Interfaces |
-
-### Security & Identity
-| Service | Resources |
-|---------|-----------|
-| IAM | Users, Roles, Policies, Groups, Instance Profiles |
-| KMS | Keys |
-| ACM | Certificates |
-| Secrets Manager | Secrets |
-| SSM | Parameters |
-| Cognito | User Pools, Users |
-| GuardDuty | Detectors, Findings |
-| WAF | Web ACLs |
-| Inspector | Findings |
-| Security Hub | Findings |
-| Firewall Manager | Policies |
-| Network Firewall | Firewalls, Firewall Policies, Rule Groups |
-| IAM Access Analyzer | Analyzers, Findings |
-| Detective | Graphs, Investigations |
-| Macie | Classification Jobs, Findings, Buckets |
-
-### Integration
-| Service | Resources |
-|---------|-----------|
-| SQS | Queues |
-| SNS | Topics, Subscriptions |
-| EventBridge | Event Buses, Rules |
-| Step Functions | State Machines, Executions |
-| Kinesis | Streams |
-| Transfer Family | Servers, Users |
-| DataSync | Tasks, Locations, Task Executions |
-
-### Management & Monitoring
-| Service | Resources |
-|---------|-----------|
-| CloudFormation | Stacks, Events, Resources, Outputs |
-| CloudWatch | Alarms, Log Groups, Log Streams |
-| CloudTrail | Trails, Events |
-| AWS Config | Rules |
-| AWS Health | Events |
-| X-Ray | Groups |
-| Service Quotas | Services, Quotas |
-| CodeBuild | Projects, Builds |
-| CodePipeline | Pipelines, Executions |
-| AWS Backup | Plans, Vaults, Selections, Protected Resources, Backup Jobs, Copy Jobs, Restore Jobs, Recovery Points |
-| Organizations | Accounts, OUs, Policies, Roots |
-| License Manager | Configurations, Licenses, Grants |
-
-### Cost Management
-| Service | Resources |
-|---------|-----------|
-| RI/SP | Reserved Instances, Savings Plans |
-| Cost Explorer | Costs, Anomalies, Monitors |
-| Compute Optimizer | Summary, Recommendations |
-| Trusted Advisor | Recommendations |
-| Budgets | Budgets, Notifications |
-
-## Service Aliases
-
-Quick shortcuts for common services:
-
-| Alias | Service |
-|-------|---------|
-| `cfn`, `cf` | CloudFormation |
-| `sg` | EC2 Security Groups |
-| `asg` | Auto Scaling |
-| `cw` | CloudWatch |
-| `logs` | CloudWatch Log Groups |
-| `ddb` | DynamoDB |
-| `sm` | Secrets Manager |
-| `r53` | Route 53 |
-| `eb` | EventBridge |
-| `sfn` | Step Functions |
-| `sq`, `quotas` | Service Quotas |
-| `apigw`, `api` | API Gateway |
-| `elb`, `alb`, `nlb` | Elastic Load Balancing |
-| `redis`, `cache` | ElastiCache |
-| `es`, `elasticsearch` | OpenSearch |
-| `cdn`, `dist` | CloudFront |
-| `gd` | GuardDuty |
-| `build`, `cb` | CodeBuild |
-| `pipeline`, `cp` | CodePipeline |
-| `waf` | WAF |
-| `ce`, `cost-explorer` | Cost Explorer |
-| `co` | Compute Optimizer |
-| `ta` | Trusted Advisor |
-| `ri` | Reserved Instances |
-| `sp` | Savings Plans |
-| `odcr` | Capacity Reservations |
-| `tgw` | Transit Gateways |
-| `agentcore` | Bedrock AgentCore |
-| `kb` | Bedrock Agent Knowledge Bases |
-| `agent` | Bedrock Agent Agents |
-| `models` | Bedrock Foundation Models |
-| `guardrail` | Bedrock Guardrails |
-
-## Configuration
-
-claws uses your standard AWS configuration:
-
-- `~/.aws/credentials` - AWS credentials
-- `~/.aws/config` - AWS configuration (region, profile)
-- Environment variables: `AWS_PROFILE`, `AWS_REGION`, `AWS_ACCESS_KEY_ID`, etc.
-
-### Configuration File
-
-Optional settings can be stored in `~/.config/claws/config.yaml`:
-
-```yaml
-timeouts:
- aws_init: 10s # AWS initialization timeout (default: 5s)
- multi_region_fetch: 60s # Multi-region parallel fetch timeout (default: 30s)
- tag_search: 45s # Tag search timeout (default: 30s)
- metrics_load: 30s # CloudWatch metrics load timeout (default: 30s)
- log_fetch: 15s # CloudWatch Logs fetch timeout (default: 10s)
-
-concurrency:
- max_fetches: 100 # Max concurrent API fetches (default: 50)
-
-cloudwatch:
- window: 15m # Metrics data window period (default: 15m)
-
-autosave:
- enabled: true # Save region/profile/theme on change (default: false)
-
-startup: # Applied on launch if present
- profiles: # Multiple profiles supported
- - production
- regions:
- - us-east-1
- - us-west-2
-
-theme: nord # Preset: dark, light, nord, dracula, gruvbox, catppuccin
-
-# Or use preset with custom overrides:
-# theme:
-# preset: dracula
-# primary: "#ff79c6"
-# danger: "#ff5555"
-```
-
-**Available Theme Presets:**
-
-| Preset | Description |
-|--------|-------------|
-| `dark` | Default dark theme (pink/magenta accents) |
-| `light` | For light-background terminals |
-| `nord` | Nordic, calm blue palette |
-| `dracula` | Popular dark theme (purple/pink) |
-| `gruvbox` | Retro, warm earth tones |
-| `catppuccin` | Modern pastel (Mocha variant) |
-
-The config file is **not created automatically**. Create it manually if needed.
-
-CLI flags (`-p`, `-r`, `-t`, `--autosave`, `--no-autosave`) override config file settings.
-
-For required IAM permissions, see [docs/iam-permissions.md](docs/iam-permissions.md).
-
-## Architecture
-
-claws uses a simple architecture with custom implementations for each service:
-
-```
-claws/
-├── cmd/claws/ # Entry point
-├── internal/
-│ ├── app/ # Main TUI application
-│ ├── aws/ # AWS client management + helpers
-│ ├── action/ # Action framework
-│ ├── dao/ # Data Access Object interface
-│ ├── log/ # Structured logging (slog-based)
-│ ├── registry/ # Service registry + aliases
-│ ├── render/ # Renderer interface
-│ ├── ui/ # Theme system
-│ └── view/ # View components
-└── custom/ # Service implementations (DAO + Renderer + Actions)
-```
-
-See [docs/architecture.md](docs/architecture.md) for details.
+| Document | Description |
+|----------|-------------|
+| [Key Bindings](docs/keybindings.md) | Complete keyboard shortcuts reference |
+| [Supported Services](docs/services.md) | All 69 services and 163 resources |
+| [Configuration](docs/configuration.md) | Config file, themes, and options |
+| [IAM Permissions](docs/iam-permissions.md) | Required AWS permissions |
+| [Architecture](docs/architecture.md) | Internal design and structure |
+| [Adding Resources](docs/adding-resources.md) | Guide for contributors |
## Development
### Prerequisites
- Go 1.25+
-- [Task](https://taskfile.dev/) (optional, for task runner)
+- [Task](https://taskfile.dev/) (optional)
### Commands
@@ -486,34 +116,13 @@ See [docs/architecture.md](docs/architecture.md) for details.
task build # Build binary
task run # Run the application
task test # Run tests
-task test-cover # Run tests with coverage
task lint # Run linters
-task fmt # Format code
-task clean # Clean build artifacts
```
-### Adding New Resources
-
-See [docs/adding-resources.md](docs/adding-resources.md) for a guide on adding new AWS resources.
-
-### Releasing
-
-Releases are automated via GoReleaser. Tag format controls which platforms are built:
-
-| Tag | Platforms Built |
-|-----|-----------------|
-| `v0.1.0` | All (linux/darwin arm64+amd64, windows) |
-| `v0.1.0-rc1` | ARM64 only (linux/darwin) |
-| `v0.1.0-rc1-amd64` | ARM64 + AMD64 (linux/darwin) |
-| `v0.1.0-rc1-win` | ARM64 + Windows |
-| `v0.1.0-rc1-all` | All platforms |
-
-Prereleases (tags containing `-`) skip Homebrew publishing.
-
## Tech Stack
-- **TUI Framework**: [Bubbletea](https://github.com/charmbracelet/bubbletea) + [Lipgloss](https://github.com/charmbracelet/lipgloss) + [Bubbles](https://github.com/charmbracelet/bubbles)
-- **AWS SDK**: [AWS SDK for Go v2](https://github.com/aws/aws-sdk-go-v2)
+- **TUI**: [Bubbletea](https://github.com/charmbracelet/bubbletea) + [Lipgloss](https://github.com/charmbracelet/lipgloss)
+- **AWS**: [AWS SDK for Go v2](https://github.com/aws/aws-sdk-go-v2)
## License
diff --git a/Taskfile.yml b/Taskfile.yml
index 8cac713a..c31b5055 100644
--- a/Taskfile.yml
+++ b/Taskfile.yml
@@ -47,7 +47,7 @@ tasks:
- |
echo "Waiting for LocalStack to be ready..."
for i in $(seq 1 30); do
- if curl -s --connect-timeout 2 --max-time 5 http://localhost:4566/_localstack/health 2>/dev/null | grep -q '"s3": "running"'; then
+ if curl -s --connect-timeout 2 --max-time 5 http://localhost:4566/_localstack/health 2>/dev/null | grep -qE '"s3": "(available|running)"'; then
echo "LocalStack started at http://localhost:4566"
exit 0
fi
@@ -92,7 +92,48 @@ tasks:
- ./claws
demo:record:
- desc: Record demo.gif using VHS + LocalStack (Linux only - --network host required)
+ desc: Record all demos (gif + screenshots) using VHS + LocalStack
+ deps: [build, localstack:start, localstack:demo-setup]
+ preconditions:
+ - sh: '[ "$(uname -s)" = "Linux" ]'
+ msg: "demo:record requires Linux (--network host not supported on macOS/Windows)"
+ cmds:
+ - task: demo:record:gif
+ - task: demo:record:themes
+ - task: demo:record:features
+
+ demo:record:gif:
+ desc: Record demo.gif only
+ deps: [build, localstack:start, localstack:demo-setup]
+ preconditions:
+ - sh: '[ "$(uname -s)" = "Linux" ]'
+ msg: "demo:record requires Linux (--network host not supported on macOS/Windows)"
+ cmds:
+ - |
+ docker run --rm --network host \
+ -v "$(pwd)":/vhs \
+ -v "$(pwd)/scripts/demo-aws-config:/root/.aws:ro" \
+ -e AWS_ENDPOINT_URL=http://localhost:4566 \
+ -e AWS_EC2_METADATA_DISABLED=true \
+ ghcr.io/charmbracelet/vhs docs/tapes/demo.tape
+
+ demo:record:themes:
+ desc: Record theme screenshots only
+ deps: [build, localstack:start, localstack:demo-setup]
+ preconditions:
+ - sh: '[ "$(uname -s)" = "Linux" ]'
+ msg: "demo:record requires Linux (--network host not supported on macOS/Windows)"
+ cmds:
+ - |
+ docker run --rm --network host \
+ -v "$(pwd)":/vhs \
+ -v "$(pwd)/scripts/demo-aws-config:/root/.aws:ro" \
+ -e AWS_ENDPOINT_URL=http://localhost:4566 \
+ -e AWS_EC2_METADATA_DISABLED=true \
+ ghcr.io/charmbracelet/vhs docs/tapes/themes.tape
+
+ demo:record:features:
+ desc: Record feature screenshots only
deps: [build, localstack:start, localstack:demo-setup]
preconditions:
- sh: '[ "$(uname -s)" = "Linux" ]'
@@ -101,12 +142,10 @@ tasks:
- |
docker run --rm --network host \
-v "$(pwd)":/vhs \
+ -v "$(pwd)/scripts/demo-aws-config:/root/.aws:ro" \
-e AWS_ENDPOINT_URL=http://localhost:4566 \
- -e AWS_ACCESS_KEY_ID=test \
- -e AWS_SECRET_ACCESS_KEY=test \
- -e AWS_DEFAULT_REGION=us-east-1 \
-e AWS_EC2_METADATA_DISABLED=true \
- ghcr.io/charmbracelet/vhs:v0.10.0 cassette.tape
+ ghcr.io/charmbracelet/vhs docs/tapes/features.tape
test-localstack:
desc: Run integration tests with LocalStack
diff --git a/demo.gif b/demo.gif
deleted file mode 100644
index 45113d13..00000000
Binary files a/demo.gif and /dev/null differ
diff --git a/docs/configuration.md b/docs/configuration.md
new file mode 100644
index 00000000..0951f916
--- /dev/null
+++ b/docs/configuration.md
@@ -0,0 +1,121 @@
+# Configuration
+
+## AWS Credentials
+
+claws uses your standard AWS configuration:
+
+- `~/.aws/credentials` - AWS credentials
+- `~/.aws/config` - AWS configuration (region, profile)
+- Environment variables: `AWS_PROFILE`, `AWS_REGION`, `AWS_ACCESS_KEY_ID`, etc.
+
+## Configuration File
+
+Optional settings can be stored in `~/.config/claws/config.yaml`:
+
+```yaml
+timeouts:
+ aws_init: 10s # AWS initialization timeout (default: 5s)
+ multi_region_fetch: 60s # Multi-region parallel fetch timeout (default: 30s)
+ tag_search: 45s # Tag search timeout (default: 30s)
+ metrics_load: 30s # CloudWatch metrics load timeout (default: 30s)
+ log_fetch: 15s # CloudWatch Logs fetch timeout (default: 10s)
+
+concurrency:
+ max_fetches: 100 # Max concurrent API fetches (default: 50)
+
+cloudwatch:
+ window: 15m # Metrics data window period (default: 15m)
+
+autosave:
+ enabled: true # Save region/profile/theme on change (default: false)
+
+startup: # Applied on launch if present
+ profiles: # Multiple profiles supported
+ - production
+ regions:
+ - us-east-1
+ - us-west-2
+
+theme: nord # Preset: dark, light, nord, dracula, gruvbox, catppuccin
+
+# Or use preset with custom overrides:
+# theme:
+# preset: dracula
+# primary: "#ff79c6"
+# danger: "#ff5555"
+```
+
+The config file is **not created automatically**. Create it manually if needed.
+
+CLI flags (`-p`, `-r`, `-t`, `--autosave`, `--no-autosave`) override config file settings.
+
+## Themes
+
+claws includes 6 built-in color themes:
+
+| Theme | Description |
+|-------|-------------|
+| `dark` | Default dark theme (pink/magenta accents) |
+| `light` | For light-background terminals |
+| `nord` | Nordic, calm blue palette |
+| `dracula` | Popular dark theme (purple/pink) |
+| `gruvbox` | Retro, warm earth tones |
+| `catppuccin` | Modern pastel (Mocha variant) |
+
+### Theme Previews
+
+| dark | light | nord |
+|------|-------|------|
+|  |  |  |
+
+| dracula | gruvbox | catppuccin |
+|---------|---------|------------|
+|  |  |  |
+
+### Switching Themes
+
+```bash
+# Via command line
+claws -t nord
+
+# Via command mode (runtime)
+:theme dracula
+```
+
+If autosave is enabled, theme changes are persisted to the config file.
+
+### Custom Theme Colors
+
+Override specific colors from a preset:
+
+```yaml
+theme:
+ preset: dracula
+ primary: "#ff79c6"
+ danger: "#ff5555"
+ success: "#50fa7b"
+```
+
+## Read-Only Mode
+
+Disable all destructive actions:
+
+```bash
+# Via flag
+claws --read-only
+
+# Via environment variable
+CLAWS_READ_ONLY=1 claws
+```
+
+## Debug Logging
+
+Enable debug logging to a file:
+
+```bash
+claws -l debug.log
+```
+
+## IAM Permissions
+
+For required IAM permissions, see [iam-permissions.md](iam-permissions.md).
diff --git a/docs/images/actions-menu.png b/docs/images/actions-menu.png
new file mode 100644
index 00000000..36ebf96d
Binary files /dev/null and b/docs/images/actions-menu.png differ
diff --git a/docs/images/demo.gif b/docs/images/demo.gif
new file mode 100644
index 00000000..37987f59
Binary files /dev/null and b/docs/images/demo.gif differ
diff --git a/docs/images/detail-view.png b/docs/images/detail-view.png
new file mode 100644
index 00000000..dc12095b
Binary files /dev/null and b/docs/images/detail-view.png differ
diff --git a/docs/images/multi-account-region.png b/docs/images/multi-account-region.png
new file mode 100644
index 00000000..a7ef7b8c
Binary files /dev/null and b/docs/images/multi-account-region.png differ
diff --git a/docs/images/multi-profile.png b/docs/images/multi-profile.png
new file mode 100644
index 00000000..99b1cc36
Binary files /dev/null and b/docs/images/multi-profile.png differ
diff --git a/docs/images/multi-region-data.png b/docs/images/multi-region-data.png
new file mode 100644
index 00000000..2adc098c
Binary files /dev/null and b/docs/images/multi-region-data.png differ
diff --git a/docs/images/multi-region.png b/docs/images/multi-region.png
new file mode 100644
index 00000000..2021fa6d
Binary files /dev/null and b/docs/images/multi-region.png differ
diff --git a/docs/images/resource-browser.png b/docs/images/resource-browser.png
new file mode 100644
index 00000000..bf4a4a47
Binary files /dev/null and b/docs/images/resource-browser.png differ
diff --git a/docs/images/theme-catppuccin.png b/docs/images/theme-catppuccin.png
new file mode 100644
index 00000000..45aaa9d0
Binary files /dev/null and b/docs/images/theme-catppuccin.png differ
diff --git a/docs/images/theme-dark.png b/docs/images/theme-dark.png
new file mode 100644
index 00000000..f5c431ac
Binary files /dev/null and b/docs/images/theme-dark.png differ
diff --git a/docs/images/theme-dracula.png b/docs/images/theme-dracula.png
new file mode 100644
index 00000000..06e93721
Binary files /dev/null and b/docs/images/theme-dracula.png differ
diff --git a/docs/images/theme-gruvbox.png b/docs/images/theme-gruvbox.png
new file mode 100644
index 00000000..46deae7e
Binary files /dev/null and b/docs/images/theme-gruvbox.png differ
diff --git a/docs/images/theme-light.png b/docs/images/theme-light.png
new file mode 100644
index 00000000..b4c61858
Binary files /dev/null and b/docs/images/theme-light.png differ
diff --git a/docs/images/theme-nord.png b/docs/images/theme-nord.png
new file mode 100644
index 00000000..86343c39
Binary files /dev/null and b/docs/images/theme-nord.png differ
diff --git a/docs/keybindings.md b/docs/keybindings.md
new file mode 100644
index 00000000..a4566768
--- /dev/null
+++ b/docs/keybindings.md
@@ -0,0 +1,116 @@
+# Key Bindings
+
+Complete reference for all keyboard shortcuts in claws.
+
+## General Navigation
+
+| Key | Action |
+|-----|--------|
+| `j` / `k` | Navigate up/down |
+| `h` / `l` | Navigate within category (service list) |
+| `Enter` / `d` | View resource details |
+| `Esc` | Go back |
+| `q` / `Ctrl+c` | Quit |
+
+## Views & Modes
+
+| Key | Action |
+|-----|--------|
+| `:` | Command mode (e.g., `:ec2/instances`) |
+| `:` + `Enter` | Go to dashboard (home) |
+| `~` | Go to dashboard (from service browser) |
+| `:services` | Go to service browser |
+| `/` | Filter mode (fuzzy search) |
+| `?` | Show help |
+
+## Resource Browser
+
+| Key | Action |
+|-----|--------|
+| `Tab` | Next resource type |
+| `1-9` | Switch to resource type by number |
+| `a` | Open actions menu |
+| `m` | Mark resource for comparison |
+| `d` | Describe (or diff if marked) |
+| `c` | Clear filter and mark |
+| `N` | Load next page (pagination) |
+| `M` | Toggle inline metrics (EC2, RDS, Lambda) |
+| `y` | Copy resource ID to clipboard |
+| `Y` | Copy resource ARN to clipboard |
+| `Ctrl+r` | Refresh (including metrics) |
+
+## Profile & Region
+
+| Key | Action |
+|-----|--------|
+| `R` | Select AWS region(s) (multi-select supported) |
+| `P` | Select AWS profile(s) (multi-select supported) |
+
+## Commands
+
+| Command | Action |
+|---------|--------|
+| `:q` / `:quit` | Quit |
+| `:login [name]` | AWS console login (default: `claws-login` profile) |
+| `:ec2/instances` | Navigate to EC2 instances |
+| `:sort ` | Sort by column (ascending) |
+| `:sort desc ` | Sort by column (descending) |
+| `:tag ` | Filter by tag (e.g., `:tag Env=prod`) |
+| `:tags` | Browse all tagged resources |
+| `:diff ` | Compare current row with named resource |
+| `:diff ` | Compare two named resources |
+| `:theme ` | Change color theme |
+| `:autosave on/off` | Enable/disable config autosave |
+
+## Mouse Support
+
+| Action | Effect |
+|--------|--------|
+| Hover | Highlight item under cursor |
+| Click | Select item / navigate |
+| Scroll wheel | Scroll through lists |
+| Click on tabs | Switch resource type |
+| Back button | Navigate back (same as Esc) |
+
+## Navigation Shortcuts (Context-dependent)
+
+These shortcuts navigate to related resources based on the current context:
+
+| Key | Action |
+|-----|--------|
+| `v` | View VPC / Versions |
+| `s` | View Subnets / Streams / Stages |
+| `g` | View Security Groups |
+| `r` | View Route Tables / Roles / Resources |
+| `e` | View Events / Executions / Endpoints |
+| `l` | View CloudWatch Logs |
+| `o` | View Outputs / Operations |
+| `i` | View Images / Indexes |
+
+## Region Selector (`R` key)
+
+| Key | Action |
+|-----|--------|
+| `j` / `k` | Navigate up/down |
+| `Space` | Toggle region selection |
+| `a` | Select all regions |
+| `n` | Deselect all regions |
+| `/` | Filter regions |
+| `Enter` | Apply selection |
+| `Esc` | Cancel |
+
+Selected regions are queried in parallel; resources display with Region column.
+
+## Profile Selector (`P` key)
+
+| Key | Action |
+|-----|--------|
+| `j` / `k` | Navigate up/down |
+| `Space` | Toggle profile selection |
+| `l` | SSO login for selected profile |
+| `L` | Console login for selected profile (`:login`) |
+| `/` | Filter profiles |
+| `Enter` | Apply selection |
+| `Esc` | Cancel |
+
+Selected profiles are queried in parallel; resources display with Profile and Account columns.
diff --git a/docs/services.md b/docs/services.md
new file mode 100644
index 00000000..c37a3552
--- /dev/null
+++ b/docs/services.md
@@ -0,0 +1,157 @@
+# Supported Services
+
+claws supports **69 services** with **163 resources**.
+
+## Compute
+
+| Service | Resources |
+|---------|-----------|
+| EC2 | Instances, Volumes, Security Groups, Elastic IPs, Key Pairs, AMIs, Snapshots, Launch Templates, Capacity Reservations |
+| Lambda | Functions |
+| ECS | Clusters, Services, Tasks |
+| Auto Scaling | Groups, Activities |
+| App Runner | Services, Operations |
+| Batch | Job Queues, Compute Environments, Jobs, Job Definitions |
+| EMR | Clusters, Steps |
+
+## Storage & Database
+
+| Service | Resources |
+|---------|-----------|
+| S3 | Buckets |
+| S3 Vectors | Buckets, Indexes |
+| DynamoDB | Tables |
+| RDS | Instances, Snapshots |
+| Redshift | Clusters, Snapshots |
+| ElastiCache | Clusters |
+| OpenSearch | Domains |
+
+## Data & Analytics
+
+| Service | Resources |
+|---------|-----------|
+| Glue | Databases, Tables, Crawlers, Jobs, Job Runs |
+| Athena | Workgroups, Query Executions |
+| Transcribe | Jobs |
+
+## Containers & ML
+
+| Service | Resources |
+|---------|-----------|
+| ECR | Repositories, Images |
+| Bedrock | Foundation Models, Guardrails, Inference Profiles |
+| Bedrock Agent | Agents, Knowledge Bases, Data Sources, Prompts, Flows |
+| Bedrock AgentCore | Runtimes, Endpoints, Versions |
+| SageMaker | Endpoints, Notebooks, Training Jobs, Models |
+
+## Networking
+
+| Service | Resources |
+|---------|-----------|
+| VPC | VPCs, Subnets, Route Tables, Internet Gateways, NAT Gateways, VPC Endpoints, Transit Gateways, TGW Attachments |
+| Route 53 | Hosted Zones, Record Sets |
+| API Gateway | REST APIs, HTTP APIs, Stages |
+| AppSync | GraphQL APIs, Data Sources |
+| ELB | Load Balancers, Target Groups, Targets |
+| CloudFront | Distributions |
+| Direct Connect | Connections, Virtual Interfaces |
+
+## Security & Identity
+
+| Service | Resources |
+|---------|-----------|
+| IAM | Users, Roles, Policies, Groups, Instance Profiles |
+| KMS | Keys |
+| ACM | Certificates |
+| Secrets Manager | Secrets |
+| SSM | Parameters |
+| Cognito | User Pools, Users |
+| GuardDuty | Detectors, Findings |
+| WAF | Web ACLs |
+| Inspector | Findings |
+| Security Hub | Findings |
+| Firewall Manager | Policies |
+| Network Firewall | Firewalls, Firewall Policies, Rule Groups |
+| IAM Access Analyzer | Analyzers, Findings |
+| Detective | Graphs, Investigations |
+| Macie | Classification Jobs, Findings, Buckets |
+
+## Integration
+
+| Service | Resources |
+|---------|-----------|
+| SQS | Queues |
+| SNS | Topics, Subscriptions |
+| EventBridge | Event Buses, Rules |
+| Step Functions | State Machines, Executions |
+| Kinesis | Streams |
+| Transfer Family | Servers, Users |
+| DataSync | Tasks, Locations, Task Executions |
+
+## Management & Monitoring
+
+| Service | Resources |
+|---------|-----------|
+| CloudFormation | Stacks, Events, Resources, Outputs |
+| CloudWatch | Alarms, Log Groups, Log Streams |
+| CloudTrail | Trails, Events |
+| AWS Config | Rules |
+| AWS Health | Events |
+| X-Ray | Groups |
+| Service Quotas | Services, Quotas |
+| CodeBuild | Projects, Builds |
+| CodePipeline | Pipelines, Executions |
+| AWS Backup | Plans, Vaults, Selections, Protected Resources, Backup Jobs, Copy Jobs, Restore Jobs, Recovery Points |
+| Organizations | Accounts, OUs, Policies, Roots |
+| License Manager | Configurations, Licenses, Grants |
+
+## Cost Management
+
+| Service | Resources |
+|---------|-----------|
+| RI/SP | Reserved Instances, Savings Plans |
+| Cost Explorer | Costs, Anomalies, Monitors |
+| Compute Optimizer | Summary, Recommendations |
+| Trusted Advisor | Recommendations |
+| Budgets | Budgets, Notifications |
+
+---
+
+## Service Aliases
+
+Quick shortcuts for common services:
+
+| Alias | Service |
+|-------|---------|
+| `cfn`, `cf` | CloudFormation |
+| `sg` | EC2 Security Groups |
+| `asg` | Auto Scaling |
+| `cw` | CloudWatch |
+| `logs` | CloudWatch Log Groups |
+| `ddb` | DynamoDB |
+| `sm` | Secrets Manager |
+| `r53` | Route 53 |
+| `eb` | EventBridge |
+| `sfn` | Step Functions |
+| `sq`, `quotas` | Service Quotas |
+| `apigw`, `api` | API Gateway |
+| `elb`, `alb`, `nlb` | Elastic Load Balancing |
+| `redis`, `cache` | ElastiCache |
+| `es`, `elasticsearch` | OpenSearch |
+| `cdn`, `dist` | CloudFront |
+| `gd` | GuardDuty |
+| `build`, `cb` | CodeBuild |
+| `pipeline`, `cp` | CodePipeline |
+| `waf` | WAF |
+| `ce`, `cost-explorer` | Cost Explorer |
+| `co` | Compute Optimizer |
+| `ta` | Trusted Advisor |
+| `ri` | Reserved Instances |
+| `sp` | Savings Plans |
+| `odcr` | Capacity Reservations |
+| `tgw` | Transit Gateways |
+| `agentcore` | Bedrock AgentCore |
+| `kb` | Bedrock Agent Knowledge Bases |
+| `agent` | Bedrock Agent Agents |
+| `models` | Bedrock Foundation Models |
+| `guardrail` | Bedrock Guardrails |
diff --git a/docs/tapes/README.md b/docs/tapes/README.md
new file mode 100644
index 00000000..d569e9f3
--- /dev/null
+++ b/docs/tapes/README.md
@@ -0,0 +1,52 @@
+# VHS Tape Files
+
+This directory contains [VHS](https://github.com/charmbracelet/vhs) tape files for generating demo GIFs and screenshots.
+
+## Tape Files
+
+| File | Purpose | Output |
+|------|---------|--------|
+| `demo.tape` | Main demo GIF | `docs/images/demo.gif` |
+| `themes.tape` | Theme screenshots | `docs/images/theme-*.png` |
+| `features.tape` | Feature screenshots | `docs/images/*.png` |
+
+## Usage (Recommended)
+
+Use task commands from project root (requires Docker + Linux):
+
+```bash
+# Record everything (GIF + all screenshots)
+task demo:record
+
+# Record individual items
+task demo:record:gif # Main demo GIF only
+task demo:record:themes # Theme screenshots only
+task demo:record:features # Feature screenshots only
+```
+
+This automatically:
+- Builds the `claws` binary
+- Starts LocalStack with demo data
+- Runs VHS in Docker with proper environment
+
+## Manual Usage
+
+If you have VHS installed locally:
+
+```bash
+# Start LocalStack with demo data first
+task localstack:demo-setup
+task build
+
+# Then run VHS from project root
+AWS_ENDPOINT_URL=http://localhost:4566 \
+AWS_ACCESS_KEY_ID=test \
+AWS_SECRET_ACCESS_KEY=test \
+vhs docs/tapes/demo.tape
+```
+
+## Notes
+
+- `task demo:record` requires Linux (`--network host` for LocalStack access)
+- Tapes use LocalStack for demo data (no real AWS credentials needed)
+- Adjust `Sleep` durations if rendering is slow
diff --git a/cassette.tape b/docs/tapes/demo.tape
similarity index 78%
rename from cassette.tape
rename to docs/tapes/demo.tape
index 25be2796..c8cf5ebd 100644
--- a/cassette.tape
+++ b/docs/tapes/demo.tape
@@ -1,6 +1,9 @@
-Output demo.gif
+# Demo recording for claws - run via: task demo:record:gif
+# Requires LocalStack running with demo data
-Set FontSize 20
+Output docs/images/demo.gif
+
+Set FontSize 16
Set Width 1920
Set Height 1080
Set TypingSpeed 0.08
@@ -22,7 +25,7 @@ Sleep 1.5s
Enter
Sleep 2s
-Type "/demo"
+Type "/public"
Sleep 1s
Enter
Sleep 2s
diff --git a/docs/tapes/features.tape b/docs/tapes/features.tape
new file mode 100644
index 00000000..cecc590b
--- /dev/null
+++ b/docs/tapes/features.tape
@@ -0,0 +1,70 @@
+# Generate feature screenshots - run via: task demo:record:features
+# Requires LocalStack running with demo data
+
+Set FontSize 16
+Set Width 1920
+Set Height 1080
+Set TypingSpeed 0.05
+
+Type "./claws"
+Enter
+Sleep 2s
+
+Type ":"
+Sleep 0.5s
+Type "ec2/instances"
+Enter
+Sleep 2s
+Screenshot docs/images/resource-browser.png
+
+Type "d"
+Sleep 2s
+Screenshot docs/images/detail-view.png
+Escape
+Sleep 1s
+
+Type "a"
+Sleep 1.5s
+Screenshot docs/images/actions-menu.png
+Escape
+Sleep 1s
+
+Type "R"
+Sleep 1s
+Screenshot docs/images/multi-region.png
+Type "/ap-northeast-1"
+Sleep 1s
+Enter
+Sleep 0.5s
+Type " "
+Sleep 0.5s
+Enter
+Sleep 3s
+Screenshot docs/images/multi-region-data.png
+
+Type "P"
+Sleep 1s
+Screenshot docs/images/multi-profile.png
+Type "/dev"
+Sleep 1s
+Enter
+Sleep 0.5s
+Type " "
+Sleep 0.5s
+Enter
+Sleep 1s
+
+Type "R"
+Sleep 0.5s
+Type "/us-west-2"
+Sleep 1s
+Enter
+Sleep 0.5s
+Type " "
+Sleep 0.5s
+Enter
+Sleep 3s
+Screenshot docs/images/multi-account-region.png
+
+Type "q"
+Sleep 0.5s
diff --git a/docs/tapes/theme-light.tape b/docs/tapes/theme-light.tape
new file mode 100644
index 00000000..1449aed6
--- /dev/null
+++ b/docs/tapes/theme-light.tape
@@ -0,0 +1,20 @@
+Set Theme { "background": "#ffffff", "foreground": "#333333", "black": "#000000", "white": "#ffffff" }
+Set FontSize 16
+Set Width 1920
+Set Height 1080
+Set TypingSpeed 0.05s
+
+Type "./claws -t light"
+Enter
+Sleep 2s
+
+Type ":"
+Sleep 0.5s
+Type "ec2/instances"
+Enter
+Sleep 2s
+
+Screenshot docs/images/theme-light.png
+
+Type "q"
+Sleep 0.5s
diff --git a/docs/tapes/themes.tape b/docs/tapes/themes.tape
new file mode 100644
index 00000000..0bb9c135
--- /dev/null
+++ b/docs/tapes/themes.tape
@@ -0,0 +1,89 @@
+# Generate theme screenshots - run via: task demo:record:themes
+# Requires LocalStack running with demo data
+
+Set FontSize 16
+Set Width 1920
+Set Height 1080
+Set TypingSpeed 0.05
+
+# Theme: dark (default)
+Type "./claws -t dark"
+Enter
+Sleep 2s
+Type ":"
+Sleep 0.5s
+Type "ec2/instances"
+Enter
+Sleep 2s
+Screenshot docs/images/theme-dark.png
+Type "q"
+Sleep 0.5s
+
+# Theme: light (white background)
+Set Theme { "background": "#ffffff", "foreground": "#333333", "black": "#000000", "white": "#ffffff" }
+Type "./claws -t light"
+Enter
+Sleep 2s
+Type ":"
+Sleep 0.5s
+Type "ec2/instances"
+Enter
+Sleep 2s
+Screenshot docs/images/theme-light.png
+Type "q"
+Sleep 0.5s
+
+# Reset to dark background for remaining themes
+Set Theme "Builtin Dark"
+
+# Theme: nord
+Type "./claws -t nord"
+Enter
+Sleep 2s
+Type ":"
+Sleep 0.5s
+Type "ec2/instances"
+Enter
+Sleep 2s
+Screenshot docs/images/theme-nord.png
+Type "q"
+Sleep 0.5s
+
+# Theme: dracula
+Type "./claws -t dracula"
+Enter
+Sleep 2s
+Type ":"
+Sleep 0.5s
+Type "ec2/instances"
+Enter
+Sleep 2s
+Screenshot docs/images/theme-dracula.png
+Type "q"
+Sleep 0.5s
+
+# Theme: gruvbox
+Type "./claws -t gruvbox"
+Enter
+Sleep 2s
+Type ":"
+Sleep 0.5s
+Type "ec2/instances"
+Enter
+Sleep 2s
+Screenshot docs/images/theme-gruvbox.png
+Type "q"
+Sleep 0.5s
+
+# Theme: catppuccin
+Type "./claws -t catppuccin"
+Enter
+Sleep 2s
+Type ":"
+Sleep 0.5s
+Type "ec2/instances"
+Enter
+Sleep 2s
+Screenshot docs/images/theme-catppuccin.png
+Type "q"
+Sleep 0.5s
diff --git a/scripts/demo-aws-config/config b/scripts/demo-aws-config/config
new file mode 100644
index 00000000..046a399a
--- /dev/null
+++ b/scripts/demo-aws-config/config
@@ -0,0 +1,11 @@
+[default]
+region = us-east-1
+endpoint_url = http://localhost:4566
+
+[profile prod]
+region = us-east-1
+endpoint_url = http://localhost:4566
+
+[profile dev]
+region = us-west-2
+endpoint_url = http://localhost:4566
diff --git a/scripts/demo-aws-config/credentials b/scripts/demo-aws-config/credentials
new file mode 100644
index 00000000..8637b6f3
--- /dev/null
+++ b/scripts/demo-aws-config/credentials
@@ -0,0 +1,11 @@
+[default]
+aws_access_key_id = 111111111111
+aws_secret_access_key = test
+
+[prod]
+aws_access_key_id = 111111111111
+aws_secret_access_key = test
+
+[dev]
+aws_access_key_id = 222222222222
+aws_secret_access_key = test
diff --git a/scripts/localstack-demo-cleanup.sh b/scripts/localstack-demo-cleanup.sh
index 5f7cc6d1..9583cad4 100755
--- a/scripts/localstack-demo-cleanup.sh
+++ b/scripts/localstack-demo-cleanup.sh
@@ -20,129 +20,98 @@ if [[ "${AWS_ENDPOINT_URL:-}" != "http://localhost:4566" ]]; then
error "AWS_ENDPOINT_URL must be http://localhost:4566 (got: ${AWS_ENDPOINT_URL:-})"
fi
-export AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-test}"
export AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-test}"
-export AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-us-east-1}"
export AWS_EC2_METADATA_DISABLED=true
+ACCOUNTS=("111111111111" "222222222222")
+REGIONS=("us-east-1" "us-west-2" "ap-northeast-1")
+
aws_cmd() {
aws --endpoint-url="${AWS_ENDPOINT_URL}" "$@"
}
-log "=== claws LocalStack Demo Cleanup ==="
-
-log "Terminating EC2 instances..."
-INSTANCES=$(aws_cmd ec2 describe-instances \
- --filters "Name=tag:Project,Values=claws-demo" "Name=instance-state-name,Values=pending,running,stopping,stopped" \
- --query 'Reservations[].Instances[].InstanceId' --output text 2>/dev/null || echo "")
-if [[ -n "$INSTANCES" ]]; then
- for id in $INSTANCES; do
- if aws_cmd ec2 terminate-instances --instance-ids "$id" 2>/dev/null; then
- log " Terminating: $id"
- else
- track_error "Failed to terminate: $id"
- fi
+cleanup_account_region() {
+ local account="$1"
+ local region="$2"
+ export AWS_ACCESS_KEY_ID="$account"
+ export AWS_DEFAULT_REGION="$region"
+
+ log "Cleaning $account / $region..."
+
+ local instances=$(aws_cmd ec2 describe-instances \
+ --filters "Name=tag:Project,Values=claws-demo" "Name=instance-state-name,Values=pending,running,stopping,stopped" \
+ --query 'Reservations[].Instances[].InstanceId' --output text 2>/dev/null || echo "")
+ if [[ -n "$instances" ]]; then
+ for id in $instances; do
+ aws_cmd ec2 terminate-instances --instance-ids "$id" 2>/dev/null && log " Terminated: $id" || track_error "Failed: $id"
+ done
+ sleep 2
+ fi
+
+ local sgs=$(aws_cmd ec2 describe-security-groups \
+ --filters "Name=tag:Project,Values=claws-demo" \
+ --query 'SecurityGroups[].GroupId' --output text 2>/dev/null || echo "")
+ for sg in $sgs; do
+ aws_cmd ec2 delete-security-group --group-id "$sg" 2>/dev/null && log " Deleted SG: $sg" || track_error "Failed SG: $sg"
done
- log " Waiting for instances to terminate..."
- for i in $(seq 1 30); do
- # shellcheck disable=SC2086 # Word splitting intended for multiple IDs
- REMAINING=$(aws_cmd ec2 describe-instances \
- --instance-ids $INSTANCES \
- --filters "Name=instance-state-name,Values=pending,running,shutting-down,stopping,stopped" \
- --query 'Reservations[].Instances[].InstanceId' --output text 2>/dev/null || echo "")
- if [[ -z "$REMAINING" ]]; then
- log " All instances terminated"
- break
- fi
- if [[ $i -eq 30 ]]; then
- track_error "Timeout waiting for instances to terminate"
+
+ local subnets=$(aws_cmd ec2 describe-subnets \
+ --filters "Name=tag:Project,Values=claws-demo" \
+ --query 'Subnets[].SubnetId' --output text 2>/dev/null || echo "")
+ for subnet in $subnets; do
+ aws_cmd ec2 delete-subnet --subnet-id "$subnet" 2>/dev/null && log " Deleted subnet: $subnet" || track_error "Failed subnet: $subnet"
+ done
+
+ local rts=$(aws_cmd ec2 describe-route-tables \
+ --filters "Name=tag:Project,Values=claws-demo" \
+ --query 'RouteTables[].RouteTableId' --output text 2>/dev/null || echo "")
+ for rt in $rts; do
+ local assocs=$(aws_cmd ec2 describe-route-tables --route-table-ids "$rt" \
+ --query 'RouteTables[0].Associations[?!Main].RouteTableAssociationId' --output text 2>/dev/null || echo "")
+ for assoc in $assocs; do
+ aws_cmd ec2 disassociate-route-table --association-id "$assoc" 2>/dev/null || true
+ done
+ aws_cmd ec2 delete-route-table --route-table-id "$rt" 2>/dev/null && log " Deleted RT: $rt" || track_error "Failed RT: $rt"
+ done
+
+ local igws=$(aws_cmd ec2 describe-internet-gateways \
+ --filters "Name=tag:Project,Values=claws-demo" \
+ --query 'InternetGateways[].InternetGatewayId' --output text 2>/dev/null || echo "")
+ for igw in $igws; do
+ local vpc=$(aws_cmd ec2 describe-internet-gateways --internet-gateway-ids "$igw" \
+ --query 'InternetGateways[0].Attachments[0].VpcId' --output text 2>/dev/null || echo "")
+ if [[ -n "$vpc" && "$vpc" != "None" ]]; then
+ aws_cmd ec2 detach-internet-gateway --internet-gateway-id "$igw" --vpc-id "$vpc" 2>/dev/null || true
fi
- sleep 1
+ aws_cmd ec2 delete-internet-gateway --internet-gateway-id "$igw" 2>/dev/null && log " Deleted IGW: $igw" || track_error "Failed IGW: $igw"
done
-fi
-
-log "Deleting Security Groups..."
-SGS=$(aws_cmd ec2 describe-security-groups \
- --filters "Name=tag:Project,Values=claws-demo" \
- --query 'SecurityGroups[].GroupId' --output text 2>/dev/null || echo "")
-for sg in $SGS; do
- if aws_cmd ec2 delete-security-group --group-id "$sg" 2>/dev/null; then
- log " Deleted: $sg"
- else
- track_error "Failed to delete SG: $sg"
- fi
-done
+
+ local vpcs=$(aws_cmd ec2 describe-vpcs \
+ --filters "Name=tag:Project,Values=claws-demo" \
+ --query 'Vpcs[].VpcId' --output text 2>/dev/null || echo "")
+ for vpc in $vpcs; do
+ aws_cmd ec2 delete-vpc --vpc-id "$vpc" 2>/dev/null && log " Deleted VPC: $vpc" || track_error "Failed VPC: $vpc"
+ done
+}
-log "Deleting Subnets..."
-SUBNETS=$(aws_cmd ec2 describe-subnets \
- --filters "Name=tag:Project,Values=claws-demo" \
- --query 'Subnets[].SubnetId' --output text 2>/dev/null || echo "")
-for subnet in $SUBNETS; do
- if aws_cmd ec2 delete-subnet --subnet-id "$subnet" 2>/dev/null; then
- log " Deleted: $subnet"
- else
- track_error "Failed to delete subnet: $subnet"
- fi
-done
+log "=== claws LocalStack Demo Cleanup ==="
-log "Deleting Route Tables..."
-RTS=$(aws_cmd ec2 describe-route-tables \
- --filters "Name=tag:Project,Values=claws-demo" \
- --query 'RouteTables[].RouteTableId' --output text 2>/dev/null || echo "")
-for rt in $RTS; do
- ASSOCS=$(aws_cmd ec2 describe-route-tables --route-table-ids "$rt" \
- --query 'RouteTables[0].Associations[?!Main].RouteTableAssociationId' --output text 2>/dev/null || echo "")
- for assoc in $ASSOCS; do
- aws_cmd ec2 disassociate-route-table --association-id "$assoc" 2>/dev/null || track_error "Failed to disassociate: $assoc"
+for account in "${ACCOUNTS[@]}"; do
+ for region in "${REGIONS[@]}"; do
+ cleanup_account_region "$account" "$region"
done
- if aws_cmd ec2 delete-route-table --route-table-id "$rt" 2>/dev/null; then
- log " Deleted: $rt"
- else
- track_error "Failed to delete RT: $rt"
- fi
-done
-
-log "Detaching and deleting Internet Gateways..."
-IGWS=$(aws_cmd ec2 describe-internet-gateways \
- --filters "Name=tag:Project,Values=claws-demo" \
- --query 'InternetGateways[].InternetGatewayId' --output text 2>/dev/null || echo "")
-for igw in $IGWS; do
- VPC=$(aws_cmd ec2 describe-internet-gateways --internet-gateway-ids "$igw" \
- --query 'InternetGateways[0].Attachments[0].VpcId' --output text 2>/dev/null || echo "")
- if [[ -n "$VPC" && "$VPC" != "None" ]]; then
- aws_cmd ec2 detach-internet-gateway --internet-gateway-id "$igw" --vpc-id "$VPC" 2>/dev/null || track_error "Failed to detach IGW: $igw"
- fi
- if aws_cmd ec2 delete-internet-gateway --internet-gateway-id "$igw" 2>/dev/null; then
- log " Deleted: $igw"
- else
- track_error "Failed to delete IGW: $igw"
- fi
done
-log "Deleting VPCs..."
-VPCS=$(aws_cmd ec2 describe-vpcs \
- --filters "Name=tag:Project,Values=claws-demo" \
- --query 'Vpcs[].VpcId' --output text 2>/dev/null || echo "")
-for vpc in $VPCS; do
- if aws_cmd ec2 delete-vpc --vpc-id "$vpc" 2>/dev/null; then
- log " Deleted: $vpc"
- else
- track_error "Failed to delete VPC: $vpc"
- fi
-done
+export AWS_ACCESS_KEY_ID="${ACCOUNTS[0]}"
+export AWS_DEFAULT_REGION="us-east-1"
log "Deleting S3 buckets..."
for bucket in claws-demo-assets claws-demo-logs claws-demo-backups; do
- if aws_cmd s3 rb "s3://${bucket}" --force 2>/dev/null; then
- log " Deleted: $bucket"
- else
- track_error "Failed to delete bucket: $bucket"
- fi
+ aws_cmd s3 rb "s3://${bucket}" --force 2>/dev/null && log " Deleted: $bucket" || true
done
if [[ $ERRORS -gt 0 ]]; then
warn "=== Cleanup completed with $ERRORS error(s) ==="
- exit 1
else
log "=== Cleanup complete ==="
fi
diff --git a/scripts/localstack-demo-setup.sh b/scripts/localstack-demo-setup.sh
index fd20e22a..dab295a7 100755
--- a/scripts/localstack-demo-setup.sh
+++ b/scripts/localstack-demo-setup.sh
@@ -25,17 +25,38 @@ if [[ "${AWS_ENDPOINT_URL:-}" != "http://localhost:4566" ]]; then
fi
# Set credentials for LocalStack
-export AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-test}"
export AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-test}"
-export AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-us-east-1}"
export AWS_EC2_METADATA_DISABLED=true
+# Account IDs (LocalStack uses AWS_ACCESS_KEY_ID as account ID if 12 digits)
+ACCOUNT_PROD="111111111111"
+ACCOUNT_DEV="222222222222"
+
+# Regions to create resources in
+REGIONS=("us-east-1" "us-west-2" "ap-northeast-1")
+
# Common tags
DEMO_TAG="Key=Project,Value=claws-demo"
DEMO_TAG2="Key=Demo,Value=true"
aws_cmd() {
- aws --endpoint-url="${AWS_ENDPOINT_URL}" "$@"
+ local account="${AWS_ACCOUNT_ID:-$ACCOUNT_PROD}"
+ local region="${AWS_REGION:-us-east-1}"
+ AWS_ACCESS_KEY_ID="$account" aws --endpoint-url="${AWS_ENDPOINT_URL}" --region "$region" "$@"
+}
+
+aws_cmd_account() {
+ local account="$1"
+ shift
+ local region="${AWS_REGION:-us-east-1}"
+ AWS_ACCESS_KEY_ID="$account" aws --endpoint-url="${AWS_ENDPOINT_URL}" --region "$region" "$@"
+}
+
+aws_cmd_region() {
+ local region="$1"
+ shift
+ local account="${AWS_ACCOUNT_ID:-$ACCOUNT_PROD}"
+ AWS_ACCESS_KEY_ID="$account" aws --endpoint-url="${AWS_ENDPOINT_URL}" --region "$region" "$@"
}
# Wait for LocalStack to be ready
@@ -52,28 +73,31 @@ wait_localstack() {
}
# ============================================
-# VPC A: Production-like setup
+# VPC A: Production-like setup (Account: prod, Region: us-east-1)
# ============================================
create_vpc_a() {
- log "Creating VPC A (prod)..."
+ log "Creating VPC A (prod) in us-east-1..."
+ export AWS_ACCOUNT_ID="$ACCOUNT_PROD"
+ export AWS_REGION="us-east-1"
+
VPC_A=$(aws_cmd ec2 create-vpc --cidr-block 10.0.0.0/16 --query 'Vpc.VpcId' --output text)
- aws_cmd ec2 create-tags --resources "$VPC_A" --tags Key=Name,Value=claws-demo-prod ${DEMO_TAG} ${DEMO_TAG2}
+ aws_cmd ec2 create-tags --resources "$VPC_A" --tags Key=Name,Value=prod-vpc-east Key=Env,Value=prod ${DEMO_TAG} ${DEMO_TAG2}
- # Internet Gateway
IGW_A=$(aws_cmd ec2 create-internet-gateway --query 'InternetGateway.InternetGatewayId' --output text)
- aws_cmd ec2 create-tags --resources "$IGW_A" --tags Key=Name,Value=claws-demo-igw-prod ${DEMO_TAG} ${DEMO_TAG2}
+ aws_cmd ec2 create-tags --resources "$IGW_A" --tags Key=Name,Value=prod-igw-east Key=Env,Value=prod ${DEMO_TAG} ${DEMO_TAG2}
aws_cmd ec2 attach-internet-gateway --vpc-id "$VPC_A" --internet-gateway-id "$IGW_A"
- # Public Subnets
SUBNET_A1=$(aws_cmd ec2 create-subnet --vpc-id "$VPC_A" --cidr-block 10.0.1.0/24 --availability-zone us-east-1a --query 'Subnet.SubnetId' --output text)
- aws_cmd ec2 create-tags --resources "$SUBNET_A1" --tags Key=Name,Value=claws-demo-public-1a ${DEMO_TAG} ${DEMO_TAG2}
+ aws_cmd ec2 create-tags --resources "$SUBNET_A1" --tags Key=Name,Value=prod-public-web-1a Key=Env,Value=prod ${DEMO_TAG} ${DEMO_TAG2}
SUBNET_A2=$(aws_cmd ec2 create-subnet --vpc-id "$VPC_A" --cidr-block 10.0.2.0/24 --availability-zone us-east-1b --query 'Subnet.SubnetId' --output text)
- aws_cmd ec2 create-tags --resources "$SUBNET_A2" --tags Key=Name,Value=claws-demo-public-1b ${DEMO_TAG} ${DEMO_TAG2}
+ aws_cmd ec2 create-tags --resources "$SUBNET_A2" --tags Key=Name,Value=prod-public-web-1b Key=Env,Value=prod ${DEMO_TAG} ${DEMO_TAG2}
- # Private Subnet
SUBNET_A3=$(aws_cmd ec2 create-subnet --vpc-id "$VPC_A" --cidr-block 10.0.10.0/24 --availability-zone us-east-1a --query 'Subnet.SubnetId' --output text)
- aws_cmd ec2 create-tags --resources "$SUBNET_A3" --tags Key=Name,Value=claws-demo-private-1a ${DEMO_TAG} ${DEMO_TAG2}
+ aws_cmd ec2 create-tags --resources "$SUBNET_A3" --tags Key=Name,Value=prod-private-db-1a Key=Env,Value=prod ${DEMO_TAG} ${DEMO_TAG2}
+
+ SUBNET_A4=$(aws_cmd ec2 create-subnet --vpc-id "$VPC_A" --cidr-block 10.0.11.0/24 --availability-zone us-east-1b --query 'Subnet.SubnetId' --output text)
+ aws_cmd ec2 create-tags --resources "$SUBNET_A4" --tags Key=Name,Value=prod-private-db-1b Key=Env,Value=prod ${DEMO_TAG} ${DEMO_TAG2}
# Route Table (public)
RTB_A=$(aws_cmd ec2 create-route-table --vpc-id "$VPC_A" --query 'RouteTable.RouteTableId' --output text)
@@ -106,87 +130,150 @@ create_vpc_a() {
log "Creating EC2 instances in VPC A..."
- # Web servers
+ # Web servers (running)
WEB1=$(aws_cmd ec2 run-instances --image-id "$AMI" --instance-type t2.micro --subnet-id "$SUBNET_A1" --security-group-ids "$SG_WEB" --query 'Instances[0].InstanceId' --output text)
- aws_cmd ec2 create-tags --resources "$WEB1" --tags Key=Name,Value=claws-demo-web-1 Key=Role,Value=web ${DEMO_TAG} ${DEMO_TAG2}
+ aws_cmd ec2 create-tags --resources "$WEB1" --tags Key=Name,Value=prod-web-1 Key=Role,Value=web Key=Status,Value=active ${DEMO_TAG} ${DEMO_TAG2}
WEB2=$(aws_cmd ec2 run-instances --image-id "$AMI" --instance-type t2.micro --subnet-id "$SUBNET_A2" --security-group-ids "$SG_WEB" --query 'Instances[0].InstanceId' --output text)
- aws_cmd ec2 create-tags --resources "$WEB2" --tags Key=Name,Value=claws-demo-web-2 Key=Role,Value=web ${DEMO_TAG} ${DEMO_TAG2}
+ aws_cmd ec2 create-tags --resources "$WEB2" --tags Key=Name,Value=prod-web-2 Key=Role,Value=web Key=Status,Value=active ${DEMO_TAG} ${DEMO_TAG2}
- # App server
+ # App server (stopped - maintenance)
APP1=$(aws_cmd ec2 run-instances --image-id "$AMI" --instance-type t2.small --subnet-id "$SUBNET_A1" --security-group-ids "$SG_APP" --query 'Instances[0].InstanceId' --output text)
- aws_cmd ec2 create-tags --resources "$APP1" --tags Key=Name,Value=claws-demo-app-1 Key=Role,Value=app ${DEMO_TAG} ${DEMO_TAG2}
+ aws_cmd ec2 create-tags --resources "$APP1" --tags Key=Name,Value=prod-app-1 Key=Role,Value=app Key=Status,Value=maintenance ${DEMO_TAG} ${DEMO_TAG2}
+ aws_cmd ec2 stop-instances --instance-ids "$APP1" || true
- # DB server
+ # DB server (running)
DB1=$(aws_cmd ec2 run-instances --image-id "$AMI" --instance-type t2.medium --subnet-id "$SUBNET_A3" --security-group-ids "$SG_DB" --query 'Instances[0].InstanceId' --output text)
- aws_cmd ec2 create-tags --resources "$DB1" --tags Key=Name,Value=claws-demo-db-1 Key=Role,Value=db ${DEMO_TAG} ${DEMO_TAG2}
+ aws_cmd ec2 create-tags --resources "$DB1" --tags Key=Name,Value=prod-db-1 Key=Role,Value=db Key=Status,Value=active ${DEMO_TAG} ${DEMO_TAG2}
log "VPC A created: $VPC_A"
}
# ============================================
-# VPC B: Dev/Staging setup
+# VPC B: Dev Account (Account: dev, Region: us-west-2)
# ============================================
create_vpc_b() {
- log "Creating VPC B (dev)..."
+ log "Creating VPC B (dev) in us-west-2..."
+ export AWS_ACCOUNT_ID="$ACCOUNT_DEV"
+ export AWS_REGION="us-west-2"
+
VPC_B=$(aws_cmd ec2 create-vpc --cidr-block 10.1.0.0/16 --query 'Vpc.VpcId' --output text)
- aws_cmd ec2 create-tags --resources "$VPC_B" --tags Key=Name,Value=claws-demo-dev ${DEMO_TAG} ${DEMO_TAG2}
+ aws_cmd ec2 create-tags --resources "$VPC_B" --tags Key=Name,Value=dev-vpc-west Key=Env,Value=dev ${DEMO_TAG} ${DEMO_TAG2}
- # Internet Gateway
IGW_B=$(aws_cmd ec2 create-internet-gateway --query 'InternetGateway.InternetGatewayId' --output text)
- aws_cmd ec2 create-tags --resources "$IGW_B" --tags Key=Name,Value=claws-demo-igw-dev ${DEMO_TAG} ${DEMO_TAG2}
+ aws_cmd ec2 create-tags --resources "$IGW_B" --tags Key=Name,Value=dev-igw-west Key=Env,Value=dev ${DEMO_TAG} ${DEMO_TAG2}
aws_cmd ec2 attach-internet-gateway --vpc-id "$VPC_B" --internet-gateway-id "$IGW_B"
- # Subnets
- SUBNET_B1=$(aws_cmd ec2 create-subnet --vpc-id "$VPC_B" --cidr-block 10.1.1.0/24 --availability-zone us-east-1a --query 'Subnet.SubnetId' --output text)
- aws_cmd ec2 create-tags --resources "$SUBNET_B1" --tags Key=Name,Value=claws-demo-dev-1a ${DEMO_TAG} ${DEMO_TAG2}
-
- SUBNET_B2=$(aws_cmd ec2 create-subnet --vpc-id "$VPC_B" --cidr-block 10.1.2.0/24 --availability-zone us-east-1b --query 'Subnet.SubnetId' --output text)
- aws_cmd ec2 create-tags --resources "$SUBNET_B2" --tags Key=Name,Value=claws-demo-dev-1b ${DEMO_TAG} ${DEMO_TAG2}
+ SUBNET_B1=$(aws_cmd ec2 create-subnet --vpc-id "$VPC_B" --cidr-block 10.1.1.0/24 --availability-zone us-west-2a --query 'Subnet.SubnetId' --output text)
+ aws_cmd ec2 create-tags --resources "$SUBNET_B1" --tags Key=Name,Value=dev-subnet-2a Key=Env,Value=dev ${DEMO_TAG} ${DEMO_TAG2}
- SUBNET_B3=$(aws_cmd ec2 create-subnet --vpc-id "$VPC_B" --cidr-block 10.1.3.0/24 --availability-zone us-east-1c --query 'Subnet.SubnetId' --output text)
- aws_cmd ec2 create-tags --resources "$SUBNET_B3" --tags Key=Name,Value=claws-demo-dev-1c ${DEMO_TAG} ${DEMO_TAG2}
+ SUBNET_B2=$(aws_cmd ec2 create-subnet --vpc-id "$VPC_B" --cidr-block 10.1.2.0/24 --availability-zone us-west-2b --query 'Subnet.SubnetId' --output text)
+ aws_cmd ec2 create-tags --resources "$SUBNET_B2" --tags Key=Name,Value=dev-subnet-2b Key=Env,Value=dev ${DEMO_TAG} ${DEMO_TAG2}
- # Route Table
RTB_B=$(aws_cmd ec2 create-route-table --vpc-id "$VPC_B" --query 'RouteTable.RouteTableId' --output text)
- aws_cmd ec2 create-tags --resources "$RTB_B" --tags Key=Name,Value=claws-demo-rt-dev ${DEMO_TAG} ${DEMO_TAG2}
+ aws_cmd ec2 create-tags --resources "$RTB_B" --tags Key=Name,Value=dev-rt-west Key=Env,Value=dev ${DEMO_TAG} ${DEMO_TAG2}
aws_cmd ec2 create-route --route-table-id "$RTB_B" --destination-cidr-block 0.0.0.0/0 --gateway-id "$IGW_B" || true
- # Security Groups
- SG_DEV=$(aws_cmd ec2 create-security-group --group-name claws-demo-sg-dev --description "Dev all-in-one" --vpc-id "$VPC_B" --query 'GroupId' --output text)
- aws_cmd ec2 create-tags --resources "$SG_DEV" --tags Key=Name,Value=claws-demo-sg-dev ${DEMO_TAG} ${DEMO_TAG2}
-
- SG_STAGING=$(aws_cmd ec2 create-security-group --group-name claws-demo-sg-staging --description "Staging" --vpc-id "$VPC_B" --query 'GroupId' --output text)
- aws_cmd ec2 create-tags --resources "$SG_STAGING" --tags Key=Name,Value=claws-demo-sg-staging ${DEMO_TAG} ${DEMO_TAG2}
-
- SG_BASTION=$(aws_cmd ec2 create-security-group --group-name claws-demo-sg-bastion --description "Bastion host" --vpc-id "$VPC_B" --query 'GroupId' --output text)
- aws_cmd ec2 create-tags --resources "$SG_BASTION" --tags Key=Name,Value=claws-demo-sg-bastion ${DEMO_TAG} ${DEMO_TAG2}
- aws_cmd ec2 authorize-security-group-ingress --group-id "$SG_BASTION" --protocol tcp --port 22 --cidr 0.0.0.0/0 || true
+ SG_DEV=$(aws_cmd ec2 create-security-group --group-name dev-sg-all --description "Dev all-in-one" --vpc-id "$VPC_B" --query 'GroupId' --output text)
+ aws_cmd ec2 create-tags --resources "$SG_DEV" --tags Key=Name,Value=dev-sg-all Key=Env,Value=dev ${DEMO_TAG} ${DEMO_TAG2}
- # EC2 Instances
AMI="ami-12345678"
- log "Creating EC2 instances in VPC B..."
+ log "Creating EC2 instances in dev account (us-west-2)..."
DEV1=$(aws_cmd ec2 run-instances --image-id "$AMI" --instance-type t2.micro --subnet-id "$SUBNET_B1" --security-group-ids "$SG_DEV" --query 'Instances[0].InstanceId' --output text)
- aws_cmd ec2 create-tags --resources "$DEV1" --tags Key=Name,Value=claws-demo-dev-server Key=Role,Value=dev ${DEMO_TAG} ${DEMO_TAG2}
+ aws_cmd ec2 create-tags --resources "$DEV1" --tags Key=Name,Value=dev-api-server Key=Env,Value=dev ${DEMO_TAG} ${DEMO_TAG2}
- STAGING1=$(aws_cmd ec2 run-instances --image-id "$AMI" --instance-type t2.small --subnet-id "$SUBNET_B2" --security-group-ids "$SG_STAGING" --query 'Instances[0].InstanceId' --output text)
- aws_cmd ec2 create-tags --resources "$STAGING1" --tags Key=Name,Value=claws-demo-staging Key=Role,Value=staging ${DEMO_TAG} ${DEMO_TAG2}
+ DEV2=$(aws_cmd ec2 run-instances --image-id "$AMI" --instance-type t2.small --subnet-id "$SUBNET_B2" --security-group-ids "$SG_DEV" --query 'Instances[0].InstanceId' --output text)
+ aws_cmd ec2 create-tags --resources "$DEV2" --tags Key=Name,Value=dev-worker Key=Env,Value=dev ${DEMO_TAG} ${DEMO_TAG2}
log "VPC B created: $VPC_B"
}
# ============================================
-# S3 Buckets (for variety in demo)
+# VPC C: Prod Account in ap-northeast-1
+# ============================================
+create_vpc_c() {
+ log "Creating VPC C (prod) in ap-northeast-1..."
+ export AWS_ACCOUNT_ID="$ACCOUNT_PROD"
+ export AWS_REGION="ap-northeast-1"
+
+ VPC_C=$(aws_cmd ec2 create-vpc --cidr-block 10.2.0.0/16 --query 'Vpc.VpcId' --output text)
+ aws_cmd ec2 create-tags --resources "$VPC_C" --tags Key=Name,Value=prod-vpc-tokyo Key=Env,Value=prod ${DEMO_TAG} ${DEMO_TAG2}
+
+ IGW_C=$(aws_cmd ec2 create-internet-gateway --query 'InternetGateway.InternetGatewayId' --output text)
+ aws_cmd ec2 create-tags --resources "$IGW_C" --tags Key=Name,Value=prod-igw-tokyo Key=Env,Value=prod ${DEMO_TAG} ${DEMO_TAG2}
+ aws_cmd ec2 attach-internet-gateway --vpc-id "$VPC_C" --internet-gateway-id "$IGW_C"
+
+ SUBNET_C1=$(aws_cmd ec2 create-subnet --vpc-id "$VPC_C" --cidr-block 10.2.1.0/24 --availability-zone ap-northeast-1a --query 'Subnet.SubnetId' --output text)
+ aws_cmd ec2 create-tags --resources "$SUBNET_C1" --tags Key=Name,Value=prod-tokyo-1a Key=Env,Value=prod ${DEMO_TAG} ${DEMO_TAG2}
+
+ SG_TOKYO=$(aws_cmd ec2 create-security-group --group-name prod-sg-tokyo --description "Tokyo prod" --vpc-id "$VPC_C" --query 'GroupId' --output text)
+ aws_cmd ec2 create-tags --resources "$SG_TOKYO" --tags Key=Name,Value=prod-sg-tokyo Key=Env,Value=prod ${DEMO_TAG} ${DEMO_TAG2}
+
+ AMI="ami-12345678"
+
+ log "Creating EC2 instances in prod account (ap-northeast-1)..."
+
+ TOKYO1=$(aws_cmd ec2 run-instances --image-id "$AMI" --instance-type t2.micro --subnet-id "$SUBNET_C1" --security-group-ids "$SG_TOKYO" --query 'Instances[0].InstanceId' --output text)
+ aws_cmd ec2 create-tags --resources "$TOKYO1" --tags Key=Name,Value=prod-tokyo-web Key=Env,Value=prod ${DEMO_TAG} ${DEMO_TAG2}
+
+ log "VPC C created: $VPC_C"
+}
+
+# ============================================
+# S3 Buckets
# ============================================
create_s3_buckets() {
log "Creating S3 buckets..."
+ export AWS_ACCOUNT_ID="$ACCOUNT_PROD"
+ export AWS_REGION="us-east-1"
aws_cmd s3 mb s3://claws-demo-assets 2>/dev/null || true
aws_cmd s3 mb s3://claws-demo-logs 2>/dev/null || true
aws_cmd s3 mb s3://claws-demo-backups 2>/dev/null || true
}
+# ============================================
+# AWS Config for multi-profile demo
+# ============================================
+create_aws_config() {
+ log "Creating AWS config for demo profiles..."
+
+ CONFIG_DIR="$(dirname "$0")/demo-aws-config"
+ mkdir -p "$CONFIG_DIR"
+
+ cat > "$CONFIG_DIR/config" << 'AWSCONFIG'
+[default]
+region = us-east-1
+endpoint_url = http://localhost:4566
+
+[profile prod]
+region = us-east-1
+endpoint_url = http://localhost:4566
+
+[profile dev]
+region = us-west-2
+endpoint_url = http://localhost:4566
+AWSCONFIG
+
+ cat > "$CONFIG_DIR/credentials" << 'AWSCREDS'
+[default]
+aws_access_key_id = 111111111111
+aws_secret_access_key = test
+
+[prod]
+aws_access_key_id = 111111111111
+aws_secret_access_key = test
+
+[dev]
+aws_access_key_id = 222222222222
+aws_secret_access_key = test
+AWSCREDS
+
+ chmod 600 "$CONFIG_DIR/credentials"
+ log "AWS config created at: $CONFIG_DIR"
+}
+
# ============================================
# Main
# ============================================
@@ -205,19 +292,21 @@ main() {
create_vpc_a
create_vpc_b
+ create_vpc_c
create_s3_buckets
+ create_aws_config
log "=== Demo setup complete! ==="
log ""
log "Resources created:"
- log " - 2 VPCs (prod, dev)"
- log " - 6 Subnets"
- log " - 4 Route Tables"
- log " - 2 Internet Gateways"
- log " - 6 Security Groups"
- log " - 6 EC2 Instances"
+ log " - 3 VPCs (prod us-east-1, dev us-west-2, prod ap-northeast-1)"
+ log " - 2 Accounts (111111111111=prod, 222222222222=dev)"
+ log " - 3 Regions (us-east-1, us-west-2, ap-northeast-1)"
+ log " - 7 EC2 Instances across accounts/regions"
log " - 3 S3 Buckets"
log ""
+ log "AWS config created at: scripts/demo-aws-config/"
+ log ""
log "Run claws with:"
log " AWS_ENDPOINT_URL=http://localhost:4566 ./claws"
}