bt-cli 0.4.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. bt_cli/__init__.py +3 -0
  2. bt_cli/cli.py +830 -0
  3. bt_cli/commands/__init__.py +1 -0
  4. bt_cli/commands/configure.py +415 -0
  5. bt_cli/commands/learn.py +229 -0
  6. bt_cli/commands/quick.py +784 -0
  7. bt_cli/core/__init__.py +1 -0
  8. bt_cli/core/auth.py +213 -0
  9. bt_cli/core/client.py +313 -0
  10. bt_cli/core/config.py +393 -0
  11. bt_cli/core/config_file.py +420 -0
  12. bt_cli/core/csv_utils.py +91 -0
  13. bt_cli/core/errors.py +247 -0
  14. bt_cli/core/output.py +205 -0
  15. bt_cli/core/prompts.py +87 -0
  16. bt_cli/core/rest_debug.py +221 -0
  17. bt_cli/data/CLAUDE.md +94 -0
  18. bt_cli/data/__init__.py +0 -0
  19. bt_cli/data/skills/bt/SKILL.md +108 -0
  20. bt_cli/data/skills/entitle/SKILL.md +170 -0
  21. bt_cli/data/skills/epmw/SKILL.md +144 -0
  22. bt_cli/data/skills/pra/SKILL.md +150 -0
  23. bt_cli/data/skills/pws/SKILL.md +198 -0
  24. bt_cli/entitle/__init__.py +1 -0
  25. bt_cli/entitle/client/__init__.py +5 -0
  26. bt_cli/entitle/client/base.py +443 -0
  27. bt_cli/entitle/commands/__init__.py +24 -0
  28. bt_cli/entitle/commands/accounts.py +53 -0
  29. bt_cli/entitle/commands/applications.py +39 -0
  30. bt_cli/entitle/commands/auth.py +68 -0
  31. bt_cli/entitle/commands/bundles.py +218 -0
  32. bt_cli/entitle/commands/integrations.py +60 -0
  33. bt_cli/entitle/commands/permissions.py +70 -0
  34. bt_cli/entitle/commands/policies.py +97 -0
  35. bt_cli/entitle/commands/resources.py +131 -0
  36. bt_cli/entitle/commands/roles.py +74 -0
  37. bt_cli/entitle/commands/users.py +123 -0
  38. bt_cli/entitle/commands/workflows.py +187 -0
  39. bt_cli/entitle/models/__init__.py +31 -0
  40. bt_cli/entitle/models/bundle.py +28 -0
  41. bt_cli/entitle/models/common.py +37 -0
  42. bt_cli/entitle/models/integration.py +30 -0
  43. bt_cli/entitle/models/permission.py +27 -0
  44. bt_cli/entitle/models/policy.py +25 -0
  45. bt_cli/entitle/models/resource.py +29 -0
  46. bt_cli/entitle/models/role.py +28 -0
  47. bt_cli/entitle/models/user.py +24 -0
  48. bt_cli/entitle/models/workflow.py +55 -0
  49. bt_cli/epmw/__init__.py +1 -0
  50. bt_cli/epmw/client/__init__.py +5 -0
  51. bt_cli/epmw/client/base.py +848 -0
  52. bt_cli/epmw/commands/__init__.py +33 -0
  53. bt_cli/epmw/commands/audits.py +250 -0
  54. bt_cli/epmw/commands/auth.py +55 -0
  55. bt_cli/epmw/commands/computers.py +140 -0
  56. bt_cli/epmw/commands/events.py +233 -0
  57. bt_cli/epmw/commands/groups.py +215 -0
  58. bt_cli/epmw/commands/policies.py +673 -0
  59. bt_cli/epmw/commands/quick.py +348 -0
  60. bt_cli/epmw/commands/requests.py +224 -0
  61. bt_cli/epmw/commands/roles.py +78 -0
  62. bt_cli/epmw/commands/tasks.py +38 -0
  63. bt_cli/epmw/commands/users.py +219 -0
  64. bt_cli/epmw/models/__init__.py +1 -0
  65. bt_cli/pra/__init__.py +1 -0
  66. bt_cli/pra/client/__init__.py +5 -0
  67. bt_cli/pra/client/base.py +618 -0
  68. bt_cli/pra/commands/__init__.py +30 -0
  69. bt_cli/pra/commands/auth.py +55 -0
  70. bt_cli/pra/commands/import_export.py +442 -0
  71. bt_cli/pra/commands/jump_clients.py +139 -0
  72. bt_cli/pra/commands/jump_groups.py +146 -0
  73. bt_cli/pra/commands/jump_items.py +638 -0
  74. bt_cli/pra/commands/jumpoints.py +95 -0
  75. bt_cli/pra/commands/policies.py +197 -0
  76. bt_cli/pra/commands/quick.py +470 -0
  77. bt_cli/pra/commands/teams.py +81 -0
  78. bt_cli/pra/commands/users.py +87 -0
  79. bt_cli/pra/commands/vault.py +564 -0
  80. bt_cli/pra/models/__init__.py +27 -0
  81. bt_cli/pra/models/common.py +12 -0
  82. bt_cli/pra/models/jump_client.py +25 -0
  83. bt_cli/pra/models/jump_group.py +15 -0
  84. bt_cli/pra/models/jump_item.py +72 -0
  85. bt_cli/pra/models/jumpoint.py +19 -0
  86. bt_cli/pra/models/team.py +14 -0
  87. bt_cli/pra/models/user.py +17 -0
  88. bt_cli/pra/models/vault.py +45 -0
  89. bt_cli/pws/__init__.py +1 -0
  90. bt_cli/pws/client/__init__.py +5 -0
  91. bt_cli/pws/client/base.py +356 -0
  92. bt_cli/pws/client/beyondinsight.py +869 -0
  93. bt_cli/pws/client/passwordsafe.py +1786 -0
  94. bt_cli/pws/commands/__init__.py +33 -0
  95. bt_cli/pws/commands/accounts.py +372 -0
  96. bt_cli/pws/commands/assets.py +311 -0
  97. bt_cli/pws/commands/auth.py +166 -0
  98. bt_cli/pws/commands/clouds.py +221 -0
  99. bt_cli/pws/commands/config.py +344 -0
  100. bt_cli/pws/commands/credentials.py +347 -0
  101. bt_cli/pws/commands/databases.py +306 -0
  102. bt_cli/pws/commands/directories.py +199 -0
  103. bt_cli/pws/commands/functional.py +298 -0
  104. bt_cli/pws/commands/import_export.py +452 -0
  105. bt_cli/pws/commands/platforms.py +118 -0
  106. bt_cli/pws/commands/quick.py +1646 -0
  107. bt_cli/pws/commands/search.py +256 -0
  108. bt_cli/pws/commands/secrets.py +1343 -0
  109. bt_cli/pws/commands/systems.py +389 -0
  110. bt_cli/pws/commands/users.py +415 -0
  111. bt_cli/pws/commands/workgroups.py +166 -0
  112. bt_cli/pws/config.py +18 -0
  113. bt_cli/pws/models/__init__.py +19 -0
  114. bt_cli/pws/models/account.py +186 -0
  115. bt_cli/pws/models/asset.py +102 -0
  116. bt_cli/pws/models/common.py +132 -0
  117. bt_cli/pws/models/system.py +121 -0
  118. bt_cli-0.4.13.dist-info/METADATA +417 -0
  119. bt_cli-0.4.13.dist-info/RECORD +121 -0
  120. bt_cli-0.4.13.dist-info/WHEEL +4 -0
  121. bt_cli-0.4.13.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,221 @@
1
+ """REST API debugging module for bt-cli.
2
+
3
+ Provides httpx event hooks to display API call details when --show-rest is enabled.
4
+ """
5
+
6
+ import json
7
+ import re
8
+ from typing import Any, Dict
9
+
10
+ import httpx
11
+ from rich.console import Console
12
+ from rich.panel import Panel
13
+ from rich.syntax import Syntax
14
+
15
+ # Global flag for REST debugging
16
+ _show_rest = False
17
+
18
+ # Console for output
19
+ _console = Console(stderr=True)
20
+
21
+
22
+ def set_show_rest(enabled: bool) -> None:
23
+ """Enable or disable REST API call display."""
24
+ global _show_rest
25
+ _show_rest = enabled
26
+
27
+
28
+ def is_show_rest() -> bool:
29
+ """Check if REST debugging is enabled."""
30
+ return _show_rest
31
+
32
+
33
+ def _sanitize_headers(headers: httpx.Headers) -> Dict[str, str]:
34
+ """Sanitize headers by fully redacting sensitive values.
35
+
36
+ Security: Never show any part of sensitive values like tokens or API keys.
37
+ """
38
+ sanitized = {}
39
+ sensitive_patterns = [
40
+ r"authorization",
41
+ r"ps-auth",
42
+ r"x-api-key",
43
+ r"api-key",
44
+ r"secret",
45
+ r"token",
46
+ r"password",
47
+ r"bearer",
48
+ ]
49
+
50
+ for key, value in headers.items():
51
+ key_lower = key.lower()
52
+ is_sensitive = any(re.search(p, key_lower) for p in sensitive_patterns)
53
+
54
+ if is_sensitive and value:
55
+ # Fully redact sensitive values - never show any characters
56
+ sanitized[key] = "[REDACTED]"
57
+ else:
58
+ sanitized[key] = value
59
+
60
+ return sanitized
61
+
62
+
63
+ def _sanitize_body(body: Any) -> Any:
64
+ """Sanitize request/response body by redacting sensitive fields.
65
+
66
+ Security: Redact credentials in form data and JSON bodies.
67
+ """
68
+ if body is None:
69
+ return None
70
+
71
+ # Sensitive field names (case-insensitive matching)
72
+ sensitive_fields = {
73
+ "password", "secret", "token", "api_key", "apikey", "api-key",
74
+ "client_secret", "client-secret", "clientsecret",
75
+ "authorization", "bearer", "credential", "credentials",
76
+ "access_token", "refresh_token", "id_token",
77
+ }
78
+
79
+ if isinstance(body, dict):
80
+ sanitized = {}
81
+ for key, value in body.items():
82
+ key_lower = key.lower().replace("-", "_")
83
+ if any(s in key_lower for s in sensitive_fields):
84
+ sanitized[key] = "[REDACTED]"
85
+ elif isinstance(value, (dict, list)):
86
+ sanitized[key] = _sanitize_body(value)
87
+ else:
88
+ sanitized[key] = value
89
+ return sanitized
90
+ elif isinstance(body, list):
91
+ return [_sanitize_body(item) for item in body]
92
+ else:
93
+ return body
94
+
95
+
96
+ def _truncate_body(body: Any, max_length: int = 500, sanitize: bool = True) -> str:
97
+ """Truncate body for display.
98
+
99
+ Args:
100
+ body: Request/response body
101
+ max_length: Maximum characters to display
102
+ sanitize: If True, redact sensitive fields before display
103
+ """
104
+ if body is None:
105
+ return "(empty)"
106
+
107
+ if isinstance(body, bytes):
108
+ try:
109
+ body = body.decode("utf-8")
110
+ except UnicodeDecodeError:
111
+ return f"(binary data, {len(body)} bytes)"
112
+
113
+ # Sanitize sensitive data before display
114
+ if sanitize and isinstance(body, (dict, list)):
115
+ body = _sanitize_body(body)
116
+
117
+ if isinstance(body, (dict, list)):
118
+ body = json.dumps(body, indent=2)
119
+
120
+ body_str = str(body)
121
+ if len(body_str) > max_length:
122
+ return body_str[:max_length] + f"\n... ({len(body_str) - max_length} more chars)"
123
+ return body_str
124
+
125
+
126
+ def log_request(request: httpx.Request) -> None:
127
+ """Log outgoing HTTP request."""
128
+ if not _show_rest:
129
+ return
130
+
131
+ # Build request info
132
+ method = request.method
133
+ url = str(request.url)
134
+ headers = _sanitize_headers(request.headers)
135
+
136
+ # Get request body if present
137
+ body = None
138
+ if request.content:
139
+ try:
140
+ body = json.loads(request.content)
141
+ except (json.JSONDecodeError, UnicodeDecodeError):
142
+ body = request.content
143
+
144
+ # Build output
145
+ lines = [
146
+ f"[bold cyan]{method}[/bold cyan] [white]{url}[/white]",
147
+ "",
148
+ "[dim]Headers:[/dim]",
149
+ ]
150
+
151
+ for key, value in headers.items():
152
+ lines.append(f" [green]{key}:[/green] {value}")
153
+
154
+ if body:
155
+ lines.append("")
156
+ lines.append("[dim]Body:[/dim]")
157
+ body_str = _truncate_body(body, max_length=300)
158
+ lines.append(f" {body_str}")
159
+
160
+ _console.print(Panel(
161
+ "\n".join(lines),
162
+ title="[bold yellow]REST Request[/bold yellow]",
163
+ border_style="yellow",
164
+ expand=False,
165
+ ))
166
+
167
+
168
+ def log_response(response: httpx.Response) -> None:
169
+ """Log HTTP response."""
170
+ if not _show_rest:
171
+ return
172
+
173
+ # Get status info
174
+ status_code = response.status_code
175
+ status_text = response.reason_phrase or ""
176
+
177
+ # Color based on status
178
+ if status_code < 300:
179
+ status_color = "green"
180
+ elif status_code < 400:
181
+ status_color = "yellow"
182
+ else:
183
+ status_color = "red"
184
+
185
+ # Get response body
186
+ try:
187
+ # Need to read the response to get body
188
+ response.read()
189
+ body = response.json()
190
+ except (json.JSONDecodeError, UnicodeDecodeError):
191
+ body = response.text if response.text else "(empty)"
192
+ except Exception:
193
+ body = "(could not read response)"
194
+
195
+ # Build output
196
+ lines = [
197
+ f"[bold {status_color}]{status_code} {status_text}[/bold {status_color}]",
198
+ "",
199
+ "[dim]Response Body:[/dim]",
200
+ _truncate_body(body, max_length=500),
201
+ ]
202
+
203
+ _console.print(Panel(
204
+ "\n".join(lines),
205
+ title="[bold blue]REST Response[/bold blue]",
206
+ border_style="blue",
207
+ expand=False,
208
+ ))
209
+ _console.print() # Add spacing between calls
210
+
211
+
212
+ def get_event_hooks() -> Dict[str, list]:
213
+ """Get httpx event hooks for request/response logging.
214
+
215
+ Usage:
216
+ client = httpx.Client(event_hooks=get_event_hooks())
217
+ """
218
+ return {
219
+ "request": [log_request],
220
+ "response": [log_response],
221
+ }
bt_cli/data/CLAUDE.md ADDED
@@ -0,0 +1,94 @@
1
+ # BT-CLI
2
+
3
+ BeyondTrust Platform CLI for Password Safe, Entitle, PRA, and EPM Windows.
4
+
5
+ ## Setup
6
+
7
+ ```bash
8
+ # From project root
9
+ source .venv/bin/activate && source .env
10
+ bt whoami # Test all connections
11
+ ```
12
+
13
+ ## Skills Available
14
+
15
+ Use these slash commands for detailed product guidance:
16
+
17
+ | Skill | Purpose |
18
+ |-------|---------|
19
+ | `/bt` | Cross-product commands (PASM onboard/offboard/search) |
20
+ | `/pws` | Password Safe - credentials, systems, secrets |
21
+ | `/pra` | PRA - jump items, vault, SSH CA |
22
+ | `/entitle` | Entitle - JIT access, bundles, workflows |
23
+ | `/epmw` | EPM Windows - computers, policies, requests |
24
+
25
+ ## Command Structure
26
+
27
+ | Product | Command | Key Operations |
28
+ |---------|---------|----------------|
29
+ | Cross-product | `bt quick` | `pasm-onboard`, `pasm-offboard`, `pasm-search` |
30
+ | Password Safe | `bt pws` | `systems`, `accounts`, `credentials`, `secrets`, `quick` |
31
+ | PRA | `bt pra` | `jump-items`, `vault`, `jump-groups`, `quick` |
32
+ | Entitle | `bt entitle` | `integrations`, `resources`, `bundles`, `permissions` |
33
+ | EPM Windows | `bt epmw` | `computers`, `groups`, `policies`, `requests`, `quick` |
34
+
35
+ ## Common Patterns
36
+
37
+ ```bash
38
+ # All list commands support JSON output
39
+ bt pws systems list -o json
40
+ bt pra jump-items shell list -o json
41
+
42
+ # Quick commands combine multiple API calls
43
+ bt pws quick checkout -s "system" -a "account"
44
+ bt pws quick onboard -n "host" -i "10.0.1.50" -w 3
45
+
46
+ # Raw output for scripting
47
+ PASSWORD=$(bt pws quick checkout -s server -a admin --raw)
48
+ ```
49
+
50
+ ## API Quirks (Gotchas)
51
+
52
+ | Product | Pagination | Response Format |
53
+ |---------|------------|-----------------|
54
+ | PWS | `limit`/`offset` | `{"TotalCount": N, "Data": [...]}` |
55
+ | Entitle | `page`/`perPage` | `{"result": [...], "pagination": {...}}` |
56
+ | PRA | `per_page`/`current_page` (1-indexed) | Array (pagination in headers) |
57
+ | EPMW | `pageNumber`/`pageSize` | `{"data": [...], "totalCount": N}` |
58
+
59
+ **Important:**
60
+ - EPMW computers: Use `archive` not `delete` (405 error)
61
+ - PRA K8s tunnels: Require Linux jumpoint
62
+ - PWS assets: Must create via workgroup endpoint
63
+ - ECM integration: PWS system name must match PRA jump item name
64
+
65
+ ## Functional vs Managed Accounts
66
+
67
+ **Functional accounts** (`bt pws functional`) - Service accounts used BY Password Safe to connect to systems for auto-management (password rotation, discovery). One functional account can manage many systems.
68
+
69
+ **Managed accounts** (`bt pws accounts`) - User accounts ON systems that Password Safe manages (stores/rotates passwords for). These are what users check out.
70
+
71
+ ```bash
72
+ # List functional accounts to find the right one for your platform
73
+ bt pws functional list
74
+
75
+ # List workgroups to find the right one
76
+ bt pws workgroups list
77
+
78
+ # Use functional account when onboarding a system
79
+ bt pws quick onboard -n "server" -i "10.0.1.50" -w <WORKGROUP_ID> -f <FUNC_ACCT_ID> -e "sudo"
80
+ ```
81
+
82
+ ## Project Structure
83
+
84
+ ```
85
+ src/bt_cli/
86
+ ├── cli.py # Root dispatcher
87
+ ├── core/ # Shared (config, auth, output)
88
+ ├── pws/ # Password Safe
89
+ ├── pra/ # PRA
90
+ ├── entitle/ # Entitle
91
+ └── epmw/ # EPM Windows
92
+ ```
93
+
94
+ Each product follows: `client/` (API), `commands/` (CLI), `models/` (Pydantic)
File without changes
@@ -0,0 +1,108 @@
1
+ ---
2
+ name: bt
3
+ description: BeyondTrust platform CLI cross-product commands. Use when working across PWS, PRA, Entitle, or EPMW together, or for PASM workflows combining Password Safe and PRA.
4
+ ---
5
+
6
+ # BT-Admin Cross-Product Commands
7
+
8
+ CLI for BeyondTrust platform: Password Safe, PRA, Entitle, EPM Windows.
9
+
10
+ ## IMPORTANT: Destructive Operations
11
+
12
+ **ALWAYS confirm with the user before running destructive commands:**
13
+ - `delete` - Removes resources permanently
14
+ - `offboard` - Removes systems/accounts from management
15
+ - `archive` - Archives computers (EPMW)
16
+ - `revoke` - Revokes permissions (Entitle)
17
+
18
+ Before executing any destructive operation:
19
+ 1. List what will be affected
20
+ 2. Ask user for explicit confirmation
21
+ 3. Never use `--force` flag without user approval
22
+
23
+ ## Setup
24
+
25
+ The CLI should be installed and configured. Test with:
26
+
27
+ ```bash
28
+ bt whoami # Verify all products are connected
29
+ ```
30
+
31
+ ## Test All Connections
32
+
33
+ ```bash
34
+ bt whoami # Test all configured products
35
+ bt whoami -o json # JSON output
36
+ ```
37
+
38
+ ## Cross-Product Quick Commands (`bt quick`)
39
+
40
+ ### PASM Search (PWS + PRA)
41
+ Find systems/accounts across both products. Shows ECM alignment (names must match for credential injection).
42
+
43
+ ```bash
44
+ bt quick pasm-search axion
45
+ bt quick pasm-search web-server -o json
46
+ ```
47
+
48
+ ### PASM Onboard (PWS + PRA)
49
+ Onboard a host to both Password Safe and PRA in one command.
50
+
51
+ **First, discover your environment IDs** (see "Discover Environment IDs" below).
52
+
53
+ ```bash
54
+ # Linux SSH host (use IDs from your environment)
55
+ bt quick pasm-onboard -n "my-server" -i "10.0.1.50" \
56
+ -w <workgroup_id> -j <jumpoint_id> -g <jump_group_id>
57
+
58
+ # Full options
59
+ bt quick pasm-onboard \
60
+ --name "web-01" \
61
+ --ip "10.0.1.100" \
62
+ --workgroup <workgroup_id> \
63
+ --jumpoint <jumpoint_id> \
64
+ --jump-group <jump_group_id> \
65
+ --functional-account <func_acct_id> \
66
+ --elevation "sudo" \
67
+ --pra-username "admin"
68
+
69
+ # Windows RDP host
70
+ bt quick pasm-onboard -n "win-srv" -i "10.0.2.10" \
71
+ -w <workgroup_id> -p <platform_id> -j <jumpoint_id> -g <jump_group_id> \
72
+ --jump-type rdp --port 3389
73
+
74
+ # Skip one product
75
+ bt quick pasm-onboard -n "pra-only" -i "10.0.1.5" -j <jumpoint_id> -g <jump_group_id> --skip-pws
76
+ bt quick pasm-onboard -n "pws-only" -i "10.0.1.6" -w <workgroup_id> --skip-pra
77
+ ```
78
+
79
+ ### PASM Offboard
80
+ Remove from both products.
81
+
82
+ ```bash
83
+ bt quick pasm-offboard -n "my-server"
84
+ bt quick pasm-offboard -n "web-01" --force
85
+ ```
86
+
87
+ ## Discover Environment IDs
88
+
89
+ Use these commands to find the IDs for your environment:
90
+
91
+ ```bash
92
+ # PWS resources
93
+ bt pws workgroups list # Find workgroup IDs
94
+ bt pws functional list # Find functional account IDs
95
+ bt pws platforms list # Find platform IDs
96
+
97
+ # PRA resources
98
+ bt pra jumpoint list # Find jumpoint IDs
99
+ bt pra jump-groups list # Find jump group IDs
100
+ bt pra vault groups list # Find vault account group IDs
101
+
102
+ # Entitle resources
103
+ bt entitle integrations list # Find integration IDs
104
+ ```
105
+
106
+ ## Product-Specific Skills
107
+
108
+ Use `/pws`, `/pra`, `/entitle`, `/epmw` for product-specific commands.
@@ -0,0 +1,170 @@
1
+ ---
2
+ name: entitle
3
+ description: Entitle commands for JIT access, bundles, workflows, and permissions. Use when working with access requests, approval workflows, or managing user entitlements.
4
+ ---
5
+
6
+ # Entitle Commands (`bt entitle`)
7
+
8
+ ## IMPORTANT: Destructive Operations
9
+
10
+ **ALWAYS confirm with the user before:**
11
+ - `bt entitle resources delete` - Deletes resource from integration
12
+ - `bt entitle bundles delete` - Deletes access bundle
13
+ - `bt entitle permissions revoke` - Revokes active permission
14
+
15
+ List affected resources first, then ask for explicit confirmation.
16
+
17
+ ## Integrations & Resources
18
+
19
+ ```bash
20
+ # List integrations (connected apps)
21
+ bt entitle integrations list
22
+ bt entitle integrations get <integration_id>
23
+
24
+ # List resources in an integration
25
+ bt entitle resources list --integration <integration_id>
26
+ bt entitle resources get <resource_id>
27
+
28
+ # Virtual integration resources (use integration ID from `bt entitle integrations list`)
29
+ bt entitle resources create-virtual \
30
+ --name "Customer-05 (Bing7)" \
31
+ --integration <virtual_integration_id> \
32
+ --source-role-id <role_id> \
33
+ --role-name "Start Session"
34
+ bt entitle resources delete <resource_id>
35
+ ```
36
+
37
+ ## Bundles
38
+
39
+ Access bundles group multiple roles across integrations.
40
+
41
+ ```bash
42
+ bt entitle bundles list
43
+ bt entitle bundles get <bundle_id>
44
+ bt entitle bundles create \
45
+ --name "Dev AWS Access" \
46
+ --description "Development AWS console access" \
47
+ --workflow <workflow_id> \
48
+ --role <role_id> \
49
+ --duration 3600
50
+ bt entitle bundles delete <bundle_id>
51
+ ```
52
+
53
+ ## Workflows
54
+
55
+ ```bash
56
+ bt entitle workflows list
57
+ bt entitle workflows get <workflow_id>
58
+ ```
59
+
60
+ **Available Workflows:**
61
+ | Name | Type | Use Case |
62
+ |------|------|----------|
63
+ | Auto Approve | Automatic | Requests <= 12 hours |
64
+ | Low sensitivity | Simple | Single approver |
65
+ | Medium Sensitivity | Standard | Standard approval |
66
+ | Production access | Multi-step | Duration-based tiers |
67
+
68
+ ## Users & Permissions
69
+
70
+ ```bash
71
+ # Users
72
+ bt entitle users list
73
+ bt entitle users get <user_id>
74
+
75
+ # Active permissions
76
+ bt entitle permissions list
77
+ bt entitle permissions list --user <user_id>
78
+ bt entitle permissions revoke <permission_id>
79
+
80
+ # Accounts
81
+ bt entitle accounts list --integration <integration_id>
82
+ ```
83
+
84
+ ## Roles & Policies
85
+
86
+ ```bash
87
+ bt entitle roles list
88
+ bt entitle roles list --resource <resource_id>
89
+ bt entitle roles get <role_id>
90
+
91
+ bt entitle policies list
92
+ bt entitle policies get <policy_id>
93
+ ```
94
+
95
+ ## Common Workflows
96
+
97
+ ### Add PRA Jump Group to Entitle
98
+
99
+ **Important:** PRA integration sync is **asynchronous** (runs hourly). After creating a new PRA jump group, either:
100
+ - Wait up to 1 hour for auto-sync
101
+ - Or manually trigger sync in Entitle UI: Integrations → PRA → Sync Now
102
+
103
+ ```bash
104
+ # 1. Find your PRA integration ID
105
+ bt entitle integrations list | grep -i pra
106
+
107
+ # 2. Trigger PRA sync in Entitle UI (or wait for hourly sync)
108
+
109
+ # 3. Find the new PRA resource
110
+ bt entitle resources list --integration <pra_integration_id>
111
+
112
+ # 4. Get "Start Sessions Only" role ID
113
+ bt entitle roles list --resource <pra_resource_id>
114
+
115
+ # 5. Add to virtual integration (find ID with `bt entitle integrations list`)
116
+ bt entitle resources create-virtual \
117
+ --name "Customer-05" \
118
+ --integration <virtual_integration_id> \
119
+ --source-role-id <role_id> \
120
+ --role-name "Start Session"
121
+ ```
122
+
123
+ ### Check User Access
124
+
125
+ ```bash
126
+ bt entitle permissions list --user "user@example.com" -o json
127
+ ```
128
+
129
+ ## Discover IDs
130
+
131
+ ```bash
132
+ # Find integration IDs
133
+ bt entitle integrations list
134
+
135
+ # Find workflow IDs
136
+ bt entitle workflows list
137
+
138
+ # Find resource IDs within an integration
139
+ bt entitle resources list --integration <integration_id>
140
+
141
+ # Find role IDs for a resource
142
+ bt entitle roles list --resource <resource_id>
143
+ ```
144
+
145
+ ## Integrations Available
146
+
147
+ - Cloud10 (Virtual Application)
148
+ - NexusDyn - Azure
149
+ - Customer Access (Virtual)
150
+ - Privileged Remote Access (PRA)
151
+ - AWS - SandBox Account
152
+ - Nexusdyn - Postgres ERP Database
153
+ - NexusDyn - EntraID
154
+
155
+ ## Supported Applications (75+)
156
+
157
+ **Cloud:** AWS, Azure, GCP, Oracle OCI
158
+ **Identity:** Okta, Entra ID, OneLogin, JumpCloud
159
+ **DevOps:** GitHub, GitLab, Jenkins, Terraform Cloud
160
+ **Databases:** Postgres, MySQL, MSSQL, MongoDB, Snowflake
161
+ **Kubernetes:** EKS, AKS, GKE, Rancher
162
+ **BeyondTrust:** Password Safe, PRA, Remote Support
163
+
164
+ ## API Notes
165
+
166
+ - Base path: `/public/v1`
167
+ - Pagination: `page`/`perPage`
168
+ - Response: `{"result": [...], "pagination": {...}}`
169
+ - All IDs are UUIDs (strings)
170
+ - Bearer token auth