0
# Validation
1
2
Comprehensive validation system with built-in validators for AWS-specific constraints, data types, and custom validation functions to ensure CloudFormation template correctness.
3
4
## Capabilities
5
6
### Core Validators
7
8
Basic data type and format validators for common CloudFormation parameter types.
9
10
```python { .api }
11
def boolean(x) -> bool:
12
"""
13
Convert value to boolean.
14
15
Args:
16
x: Value to convert (accepts various truthy/falsy formats)
17
18
Returns:
19
bool: Boolean value
20
"""
21
22
def integer(x) -> int:
23
"""
24
Validate and convert to integer.
25
26
Args:
27
x: Value to validate
28
29
Returns:
30
int: Integer value
31
32
Raises:
33
ValueError: If value cannot be converted to integer
34
"""
35
36
def positive_integer(x) -> int:
37
"""
38
Validate positive integer (>= 0).
39
40
Args:
41
x: Value to validate
42
43
Returns:
44
int: Positive integer value
45
46
Raises:
47
ValueError: If value is not a positive integer
48
"""
49
50
def integer_range(minimum: int, maximum: int) -> Callable:
51
"""
52
Create validator for integer within specified range.
53
54
Args:
55
minimum: Minimum allowed value (inclusive)
56
maximum: Maximum allowed value (inclusive)
57
58
Returns:
59
Callable: Validator function
60
"""
61
62
def double(x) -> float:
63
"""
64
Validate and convert to floating point number.
65
66
Args:
67
x: Value to validate
68
69
Returns:
70
float: Float value
71
72
Raises:
73
ValueError: If value cannot be converted to float
74
"""
75
76
def network_port(x) -> int:
77
"""
78
Validate network port number (0-65535).
79
80
Args:
81
x: Port number to validate
82
83
Returns:
84
int: Valid port number
85
86
Raises:
87
ValueError: If port is outside valid range
88
"""
89
```
90
91
### AWS-Specific Validators
92
93
Validators for AWS resource naming and format requirements.
94
95
```python { .api }
96
def s3_bucket_name(b) -> str:
97
"""
98
Validate S3 bucket name format.
99
100
Args:
101
b: Bucket name to validate
102
103
Returns:
104
str: Valid bucket name
105
106
Raises:
107
ValueError: If bucket name doesn't meet S3 requirements
108
"""
109
110
def elb_name(b) -> str:
111
"""
112
Validate Elastic Load Balancer name format.
113
114
Args:
115
b: ELB name to validate
116
117
Returns:
118
str: Valid ELB name
119
120
Raises:
121
ValueError: If name doesn't meet ELB requirements
122
"""
123
124
def encoding(encoding) -> str:
125
"""
126
Validate encoding type.
127
128
Args:
129
encoding: Encoding name to validate
130
131
Returns:
132
str: Valid encoding name
133
134
Raises:
135
ValueError: If encoding is not supported
136
"""
137
138
def json_checker(data) -> dict:
139
"""
140
Validate JSON structure.
141
142
Args:
143
data: Data to validate as JSON
144
145
Returns:
146
dict: Validated JSON data
147
148
Raises:
149
ValueError: If data is not valid JSON
150
"""
151
```
152
153
### Constraint Validators
154
155
Validators for enforcing property constraints and dependencies.
156
157
```python { .api }
158
def one_of(class_name: str, properties: dict, property: str, conditionals: list) -> None:
159
"""
160
Validate that property value is from allowed list.
161
162
Args:
163
class_name: Class name for error messages
164
properties: Properties dictionary
165
property: Property name to validate
166
conditionals: List of allowed values
167
168
Raises:
169
ValueError: If property value not in allowed list
170
"""
171
172
def mutually_exclusive(class_name: str, properties: dict, conditionals: list) -> None:
173
"""
174
Validate that only one of the specified properties is set.
175
176
Args:
177
class_name: Class name for error messages
178
properties: Properties dictionary
179
conditionals: List of mutually exclusive property names
180
181
Raises:
182
ValueError: If multiple exclusive properties are set
183
"""
184
185
def exactly_one(class_name: str, properties: dict, conditionals: list) -> None:
186
"""
187
Validate that exactly one of the specified properties is set.
188
189
Args:
190
class_name: Class name for error messages
191
properties: Properties dictionary
192
conditionals: List of property names (exactly one required)
193
194
Raises:
195
ValueError: If zero or multiple properties are set
196
"""
197
198
def check_required(class_name: str, properties: dict, conditionals: list) -> None:
199
"""
200
Validate that required properties are present.
201
202
Args:
203
class_name: Class name for error messages
204
properties: Properties dictionary
205
conditionals: List of required property names
206
207
Raises:
208
ValueError: If required properties are missing
209
"""
210
```
211
212
### Custom Validation Functions
213
214
Examples of creating custom validators for specific use cases.
215
216
```python { .api }
217
def validate_cidr_block(cidr: str) -> str:
218
"""
219
Custom validator for CIDR blocks.
220
221
Args:
222
cidr: CIDR block string
223
224
Returns:
225
str: Valid CIDR block
226
227
Raises:
228
ValueError: If CIDR format is invalid
229
"""
230
231
def validate_email(email: str) -> str:
232
"""
233
Custom validator for email addresses.
234
235
Args:
236
email: Email address string
237
238
Returns:
239
str: Valid email address
240
241
Raises:
242
ValueError: If email format is invalid
243
"""
244
245
def validate_tags(tags: dict) -> dict:
246
"""
247
Custom validator for tag dictionaries.
248
249
Args:
250
tags: Dictionary of tags
251
252
Returns:
253
dict: Valid tags dictionary
254
255
Raises:
256
ValueError: If tags format is invalid
257
"""
258
```
259
260
## Usage Examples
261
262
### Basic Type Validation
263
264
```python
265
from troposphere import Parameter
266
from troposphere.validators import positive_integer, network_port, boolean
267
268
# Parameter with positive integer validation
269
memory_size = Parameter(
270
"MemorySize",
271
Type="Number",
272
Default=128,
273
Description="Lambda function memory size in MB",
274
props={
275
"MemorySize": (positive_integer, True)
276
}
277
)
278
279
# Parameter with port validation
280
app_port = Parameter(
281
"ApplicationPort",
282
Type="Number",
283
Default=8080,
284
Description="Application port number",
285
props={
286
"ApplicationPort": (network_port, True)
287
}
288
)
289
290
# Boolean parameter with validation
291
enable_monitoring = Parameter(
292
"EnableMonitoring",
293
Type="String",
294
Default="true",
295
AllowedValues=["true", "false"],
296
Description="Enable CloudWatch monitoring",
297
props={
298
"EnableMonitoring": (boolean, False)
299
}
300
)
301
```
302
303
### AWS Resource Validation
304
305
```python
306
from troposphere.validators import s3_bucket_name, elb_name
307
from troposphere import Parameter
308
309
# S3 bucket name validation
310
bucket_name = Parameter(
311
"BucketName",
312
Type="String",
313
Description="S3 bucket name",
314
props={
315
"BucketName": (s3_bucket_name, True)
316
}
317
)
318
319
# ELB name validation
320
load_balancer_name = Parameter(
321
"LoadBalancerName",
322
Type="String",
323
Description="Load balancer name",
324
props={
325
"LoadBalancerName": (elb_name, True)
326
}
327
)
328
```
329
330
### Range Validation
331
332
```python
333
from troposphere.validators import integer_range
334
from troposphere import Parameter
335
336
# Create range validator
337
port_range_validator = integer_range(1024, 65535)
338
339
# Parameter with range validation
340
app_port = Parameter(
341
"ApplicationPort",
342
Type="Number",
343
Default=8080,
344
Description="Application port (1024-65535)",
345
props={
346
"ApplicationPort": (port_range_validator, True)
347
}
348
)
349
350
# Database storage size validation
351
storage_range_validator = integer_range(20, 1000)
352
353
db_storage = Parameter(
354
"DBStorageSize",
355
Type="Number",
356
Default=100,
357
Description="Database storage size in GB (20-1000)",
358
props={
359
"DBStorageSize": (storage_range_validator, True)
360
}
361
)
362
```
363
364
### Constraint Validation in Resources
365
366
```python
367
from troposphere import AWSProperty
368
from troposphere.validators import mutually_exclusive, exactly_one
369
370
class CustomProperty(AWSProperty):
371
props = {
372
"SourceType": (str, False),
373
"SourceValue": (str, False),
374
"SourceArn": (str, False),
375
"SourceAccount": (str, False)
376
}
377
378
def validate(self):
379
# Exactly one source must be specified
380
exactly_one(
381
self.__class__.__name__,
382
self.properties,
383
["SourceValue", "SourceArn", "SourceAccount"]
384
)
385
386
# Source type and value are mutually exclusive with ARN
387
mutually_exclusive(
388
self.__class__.__name__,
389
self.properties,
390
[["SourceType", "SourceValue"], ["SourceArn"]]
391
)
392
```
393
394
### Custom Validators
395
396
```python
397
import re
398
import ipaddress
399
from troposphere import Parameter
400
from troposphere.validators import one_of
401
402
def validate_cidr_block(cidr):
403
"""Validate CIDR block format."""
404
try:
405
ipaddress.IPv4Network(cidr, strict=False)
406
return cidr
407
except ValueError:
408
raise ValueError(f"Invalid CIDR block: {cidr}")
409
410
def validate_email(email):
411
"""Validate email address format."""
412
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
413
if not re.match(pattern, email):
414
raise ValueError(f"Invalid email address: {email}")
415
return email
416
417
def validate_instance_family(instance_type):
418
"""Validate instance type belongs to allowed families."""
419
allowed_families = ['t2', 't3', 'm5', 'c5', 'r5']
420
family = instance_type.split('.')[0]
421
if family not in allowed_families:
422
raise ValueError(f"Instance family {family} not allowed")
423
return instance_type
424
425
# Use custom validators in parameters
426
vpc_cidr = Parameter(
427
"VpcCidr",
428
Type="String",
429
Default="10.0.0.0/16",
430
Description="VPC CIDR block",
431
props={
432
"VpcCidr": (validate_cidr_block, True)
433
}
434
)
435
436
notification_email = Parameter(
437
"NotificationEmail",
438
Type="String",
439
Description="Email for notifications",
440
props={
441
"NotificationEmail": (validate_email, True)
442
}
443
)
444
445
instance_type = Parameter(
446
"InstanceType",
447
Type="String",
448
Default="t3.micro",
449
Description="EC2 instance type (restricted families)",
450
props={
451
"InstanceType": (validate_instance_family, True)
452
}
453
)
454
```
455
456
### Conditional Validation
457
458
```python
459
from troposphere import AWSObject
460
from troposphere.validators import check_required
461
462
class ConditionalResource(AWSObject):
463
props = {
464
"Type": (str, True),
465
"DatabaseConfig": (dict, False),
466
"CacheConfig": (dict, False),
467
"DatabaseEndpoint": (str, False),
468
"CacheEndpoint": (str, False)
469
}
470
471
def validate(self):
472
# Check conditional requirements based on type
473
if self.properties.get("Type") == "database":
474
check_required(
475
self.__class__.__name__,
476
self.properties,
477
["DatabaseConfig", "DatabaseEndpoint"]
478
)
479
elif self.properties.get("Type") == "cache":
480
check_required(
481
self.__class__.__name__,
482
self.properties,
483
["CacheConfig", "CacheEndpoint"]
484
)
485
```
486
487
### Validation with Error Handling
488
489
```python
490
from troposphere import Template, Parameter
491
from troposphere.validators import positive_integer
492
493
def safe_create_parameter(template, name, param_type, default_value, validator=None):
494
"""Safely create parameter with validation and error handling."""
495
try:
496
props = {}
497
if validator:
498
props[name] = (validator, True)
499
500
param = Parameter(
501
name,
502
Type=param_type,
503
Default=default_value,
504
props=props
505
)
506
507
return template.add_parameter(param)
508
509
except ValueError as e:
510
print(f"Validation error for parameter {name}: {e}")
511
raise
512
except Exception as e:
513
print(f"Error creating parameter {name}: {e}")
514
raise
515
516
# Usage
517
template = Template()
518
519
# This will succeed
520
memory_param = safe_create_parameter(
521
template, "MemorySize", "Number", 128, positive_integer
522
)
523
524
# This would fail validation
525
try:
526
invalid_param = safe_create_parameter(
527
template, "InvalidMemory", "Number", -1, positive_integer
528
)
529
except ValueError:
530
print("Caught validation error for negative memory size")
531
```