or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

argument-processing.mdcommand-system.mdcore-driver.mdcustom-commands.mderror-handling.mdhelp-system.mdindex.mdoutput-formatting.mdplugin-system.mdtesting-framework.mdutilities.md

custom-commands.mddocs/

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

```