0
# Custom Commands Framework
1
2
Extensible framework for creating custom CLI commands with built-in support for argument handling, help generation, and integration with the AWS CLI command hierarchy. This framework enables developers to extend AWS CLI with domain-specific commands and workflows.
3
4
## Capabilities
5
6
### Basic Command Class
7
8
The foundational class for creating custom AWS CLI commands with automatic integration into the command system.
9
10
```python { .api }
11
class BasicCommand(CLICommand):
12
NAME: str # Command name (required)
13
DESCRIPTION: str # Command description for help (required)
14
SYNOPSIS: str # Command synopsis for help
15
EXAMPLES: str # Usage examples for help
16
ARG_TABLE: dict # Argument definitions
17
SUBCOMMANDS: dict # Subcommand definitions
18
19
FROM_FILE: type # Helper for loading content from files
20
```
21
22
**Usage Example:**
23
```python
24
from awscli.customizations.commands import BasicCommand
25
from awscli.arguments import CustomArgument
26
27
class DeployCommand(BasicCommand):
28
NAME = 'deploy'
29
DESCRIPTION = 'Deploy application to AWS infrastructure'
30
SYNOPSIS = 'aws deploy [--environment ENV] [--dry-run] APPLICATION'
31
EXAMPLES = '''
32
Deploy to staging environment:
33
aws deploy --environment staging my-app
34
35
Perform dry run deployment:
36
aws deploy --dry-run --environment prod my-app
37
'''
38
39
ARG_TABLE = [
40
CustomArgument(
41
'application',
42
help_text='Application name to deploy',
43
positional_arg=True,
44
required=True
45
),
46
CustomArgument(
47
'environment',
48
help_text='Target environment (dev, staging, prod)',
49
choices=['dev', 'staging', 'prod'],
50
default='dev'
51
),
52
CustomArgument(
53
'dry-run',
54
help_text='Perform deployment validation without making changes',
55
action='store_true'
56
)
57
]
58
59
def _run_main(self, parsed_args, parsed_globals):
60
app_name = parsed_args.application
61
environment = parsed_args.environment
62
dry_run = parsed_args.dry_run
63
64
if dry_run:
65
print(f"Would deploy {app_name} to {environment}")
66
return 0
67
68
print(f"Deploying {app_name} to {environment}...")
69
# Deployment logic here
70
return 0
71
```
72
73
### File Content Loading
74
75
Helper class for loading documentation and configuration from external files.
76
77
```python { .api }
78
class _FromFile:
79
def __init__(self, *paths, **kwargs):
80
"""
81
Initialize file loader for documentation content.
82
83
Parameters:
84
*paths: str, file paths to load content from
85
**kwargs: additional options for content loading
86
"""
87
```
88
89
**Usage Example:**
90
```python
91
class DocumentedCommand(BasicCommand):
92
NAME = 'documented-command'
93
DESCRIPTION = FROM_FILE('docs/command-description.txt')
94
EXAMPLES = FROM_FILE('docs/command-examples.txt', 'docs/additional-examples.txt')
95
96
def _run_main(self, parsed_args, parsed_globals):
97
# Command implementation
98
return 0
99
```
100
101
## Command Development Patterns
102
103
### Simple Command Structure
104
105
For straightforward commands with minimal arguments:
106
107
```python
108
from awscli.customizations.commands import BasicCommand
109
110
class StatusCommand(BasicCommand):
111
NAME = 'status'
112
DESCRIPTION = 'Check AWS resource status'
113
114
def _run_main(self, parsed_args, parsed_globals):
115
print("Checking AWS resource status...")
116
# Status checking logic
117
return 0
118
```
119
120
### Command with Arguments
121
122
For commands requiring user input and configuration:
123
124
```python
125
class ConfigureCommand(BasicCommand):
126
NAME = 'configure-app'
127
DESCRIPTION = 'Configure application settings'
128
129
ARG_TABLE = [
130
CustomArgument(
131
'config-file',
132
help_text='Configuration file path',
133
required=True,
134
cli_type_name='string'
135
),
136
CustomArgument(
137
'region',
138
help_text='AWS region',
139
default='us-east-1'
140
),
141
CustomArgument(
142
'validate',
143
help_text='Validate configuration before applying',
144
action='store_true'
145
)
146
]
147
148
def _run_main(self, parsed_args, parsed_globals):
149
config_file = parsed_args.config_file
150
region = parsed_args.region
151
validate = parsed_args.validate
152
153
if validate:
154
if not self._validate_config(config_file):
155
return 1
156
157
self._apply_config(config_file, region)
158
return 0
159
160
def _validate_config(self, config_file):
161
# Validation logic
162
return True
163
164
def _apply_config(self, config_file, region):
165
# Configuration application logic
166
pass
167
```
168
169
### Command with Subcommands
170
171
For complex commands with multiple operations:
172
173
```python
174
class ManageCommand(BasicCommand):
175
NAME = 'manage'
176
DESCRIPTION = 'Manage AWS resources'
177
178
SUBCOMMANDS = [
179
{
180
'name': 'create',
181
'command_class': CreateResourceCommand
182
},
183
{
184
'name': 'delete',
185
'command_class': DeleteResourceCommand
186
},
187
{
188
'name': 'list',
189
'command_class': ListResourcesCommand
190
}
191
]
192
193
class CreateResourceCommand(BasicCommand):
194
NAME = 'create'
195
DESCRIPTION = 'Create new AWS resource'
196
197
ARG_TABLE = [
198
CustomArgument(
199
'resource-type',
200
help_text='Type of resource to create',
201
choices=['instance', 'bucket', 'function'],
202
required=True
203
),
204
CustomArgument(
205
'name',
206
help_text='Resource name',
207
required=True
208
)
209
]
210
211
def _run_main(self, parsed_args, parsed_globals):
212
resource_type = parsed_args.resource_type
213
name = parsed_args.name
214
215
print(f"Creating {resource_type} named {name}")
216
# Resource creation logic
217
return 0
218
```
219
220
## Advanced Command Features
221
222
### Error Handling and Exit Codes
223
224
Commands should handle errors gracefully and return appropriate exit codes:
225
226
```python
227
class RobustCommand(BasicCommand):
228
NAME = 'robust-command'
229
DESCRIPTION = 'Command with comprehensive error handling'
230
231
def _run_main(self, parsed_args, parsed_globals):
232
try:
233
self._execute_operation()
234
return 0 # Success
235
except ValidationError as e:
236
self._write_error(f"Validation failed: {e}")
237
return 1 # Validation error
238
except ServiceError as e:
239
self._write_error(f"AWS service error: {e}")
240
return 2 # Service error
241
except FileNotFoundError as e:
242
self._write_error(f"File not found: {e}")
243
return 3 # File error
244
except Exception as e:
245
self._write_error(f"Unexpected error: {e}")
246
return 255 # Unknown error
247
248
def _write_error(self, message):
249
import sys
250
sys.stderr.write(f"ERROR: {message}\n")
251
252
def _execute_operation(self):
253
# Main operation logic
254
pass
255
```
256
257
### Integration with AWS Services
258
259
Commands can integrate with AWS services using the session:
260
261
```python
262
class S3Command(BasicCommand):
263
NAME = 's3-utility'
264
DESCRIPTION = 'Custom S3 utility command'
265
266
ARG_TABLE = [
267
CustomArgument(
268
'bucket',
269
help_text='S3 bucket name',
270
required=True
271
),
272
CustomArgument(
273
'operation',
274
help_text='Operation to perform',
275
choices=['list', 'sync', 'cleanup'],
276
required=True
277
)
278
]
279
280
def _run_main(self, parsed_args, parsed_globals):
281
# Get S3 client from session
282
session = self._session
283
s3_client = session.create_client('s3')
284
285
bucket = parsed_args.bucket
286
operation = parsed_args.operation
287
288
if operation == 'list':
289
return self._list_objects(s3_client, bucket)
290
elif operation == 'sync':
291
return self._sync_bucket(s3_client, bucket)
292
elif operation == 'cleanup':
293
return self._cleanup_bucket(s3_client, bucket)
294
295
def _list_objects(self, s3_client, bucket):
296
try:
297
response = s3_client.list_objects_v2(Bucket=bucket)
298
for obj in response.get('Contents', []):
299
print(f"{obj['Key']} ({obj['Size']} bytes)")
300
return 0
301
except Exception as e:
302
self._write_error(f"Failed to list objects: {e}")
303
return 1
304
```
305
306
### Output Formatting
307
308
Commands can use AWS CLI's output formatting:
309
310
```python
311
class FormattedCommand(BasicCommand):
312
NAME = 'formatted-output'
313
DESCRIPTION = 'Command with formatted output'
314
315
def _run_main(self, parsed_args, parsed_globals):
316
# Generate data
317
data = {
318
'resources': [
319
{'name': 'resource1', 'status': 'active'},
320
{'name': 'resource2', 'status': 'inactive'}
321
],
322
'total': 2
323
}
324
325
# Use CLI's output formatting
326
from awscli.formatter import get_formatter
327
formatter = get_formatter(
328
parsed_globals.get('output', 'json'),
329
parsed_args
330
)
331
332
formatter(data)
333
return 0
334
```
335
336
## Command Registration and Integration
337
338
### Plugin-Based Registration
339
340
Commands are typically registered through the plugin system:
341
342
```python
343
# In your plugin module
344
def register_commands(cli):
345
"""Register custom commands with AWS CLI."""
346
cli.register('building-command-table.main', add_custom_commands)
347
348
def add_custom_commands(command_table, session, **kwargs):
349
"""Add custom commands to the command table."""
350
command_table['deploy'] = DeployCommand(session)
351
command_table['status'] = StatusCommand(session)
352
command_table['manage'] = ManageCommand(session)
353
```
354
355
### Direct Integration
356
357
For development and testing, commands can be integrated directly:
358
359
```python
360
from awscli.clidriver import create_clidriver
361
362
# Create CLI driver
363
driver = create_clidriver()
364
365
# Add custom command to command table
366
driver.session.register(
367
'building-command-table.main',
368
lambda command_table, session, **kwargs:
369
command_table.update({'my-command': MyCommand(session)})
370
)
371
372
# Execute command
373
exit_code = driver.main(['my-command', '--help'])
374
```