AWS CloudFormation creation library that facilitates building infrastructure templates programmatically in Python
Comprehensive validation system with built-in validators for AWS-specific constraints, data types, and custom validation functions to ensure CloudFormation template correctness.
Basic data type and format validators for common CloudFormation parameter types.
def boolean(x) -> bool:
"""
Convert value to boolean.
Args:
x: Value to convert (accepts various truthy/falsy formats)
Returns:
bool: Boolean value
"""
def integer(x) -> int:
"""
Validate and convert to integer.
Args:
x: Value to validate
Returns:
int: Integer value
Raises:
ValueError: If value cannot be converted to integer
"""
def positive_integer(x) -> int:
"""
Validate positive integer (>= 0).
Args:
x: Value to validate
Returns:
int: Positive integer value
Raises:
ValueError: If value is not a positive integer
"""
def integer_range(minimum: int, maximum: int) -> Callable:
"""
Create validator for integer within specified range.
Args:
minimum: Minimum allowed value (inclusive)
maximum: Maximum allowed value (inclusive)
Returns:
Callable: Validator function
"""
def double(x) -> float:
"""
Validate and convert to floating point number.
Args:
x: Value to validate
Returns:
float: Float value
Raises:
ValueError: If value cannot be converted to float
"""
def network_port(x) -> int:
"""
Validate network port number (0-65535).
Args:
x: Port number to validate
Returns:
int: Valid port number
Raises:
ValueError: If port is outside valid range
"""Validators for AWS resource naming and format requirements.
def s3_bucket_name(b) -> str:
"""
Validate S3 bucket name format.
Args:
b: Bucket name to validate
Returns:
str: Valid bucket name
Raises:
ValueError: If bucket name doesn't meet S3 requirements
"""
def elb_name(b) -> str:
"""
Validate Elastic Load Balancer name format.
Args:
b: ELB name to validate
Returns:
str: Valid ELB name
Raises:
ValueError: If name doesn't meet ELB requirements
"""
def encoding(encoding) -> str:
"""
Validate encoding type.
Args:
encoding: Encoding name to validate
Returns:
str: Valid encoding name
Raises:
ValueError: If encoding is not supported
"""
def json_checker(data) -> dict:
"""
Validate JSON structure.
Args:
data: Data to validate as JSON
Returns:
dict: Validated JSON data
Raises:
ValueError: If data is not valid JSON
"""Validators for enforcing property constraints and dependencies.
def one_of(class_name: str, properties: dict, property: str, conditionals: list) -> None:
"""
Validate that property value is from allowed list.
Args:
class_name: Class name for error messages
properties: Properties dictionary
property: Property name to validate
conditionals: List of allowed values
Raises:
ValueError: If property value not in allowed list
"""
def mutually_exclusive(class_name: str, properties: dict, conditionals: list) -> None:
"""
Validate that only one of the specified properties is set.
Args:
class_name: Class name for error messages
properties: Properties dictionary
conditionals: List of mutually exclusive property names
Raises:
ValueError: If multiple exclusive properties are set
"""
def exactly_one(class_name: str, properties: dict, conditionals: list) -> None:
"""
Validate that exactly one of the specified properties is set.
Args:
class_name: Class name for error messages
properties: Properties dictionary
conditionals: List of property names (exactly one required)
Raises:
ValueError: If zero or multiple properties are set
"""
def check_required(class_name: str, properties: dict, conditionals: list) -> None:
"""
Validate that required properties are present.
Args:
class_name: Class name for error messages
properties: Properties dictionary
conditionals: List of required property names
Raises:
ValueError: If required properties are missing
"""Examples of creating custom validators for specific use cases.
def validate_cidr_block(cidr: str) -> str:
"""
Custom validator for CIDR blocks.
Args:
cidr: CIDR block string
Returns:
str: Valid CIDR block
Raises:
ValueError: If CIDR format is invalid
"""
def validate_email(email: str) -> str:
"""
Custom validator for email addresses.
Args:
email: Email address string
Returns:
str: Valid email address
Raises:
ValueError: If email format is invalid
"""
def validate_tags(tags: dict) -> dict:
"""
Custom validator for tag dictionaries.
Args:
tags: Dictionary of tags
Returns:
dict: Valid tags dictionary
Raises:
ValueError: If tags format is invalid
"""from troposphere import Parameter
from troposphere.validators import positive_integer, network_port, boolean
# Parameter with positive integer validation
memory_size = Parameter(
"MemorySize",
Type="Number",
Default=128,
Description="Lambda function memory size in MB",
props={
"MemorySize": (positive_integer, True)
}
)
# Parameter with port validation
app_port = Parameter(
"ApplicationPort",
Type="Number",
Default=8080,
Description="Application port number",
props={
"ApplicationPort": (network_port, True)
}
)
# Boolean parameter with validation
enable_monitoring = Parameter(
"EnableMonitoring",
Type="String",
Default="true",
AllowedValues=["true", "false"],
Description="Enable CloudWatch monitoring",
props={
"EnableMonitoring": (boolean, False)
}
)from troposphere.validators import s3_bucket_name, elb_name
from troposphere import Parameter
# S3 bucket name validation
bucket_name = Parameter(
"BucketName",
Type="String",
Description="S3 bucket name",
props={
"BucketName": (s3_bucket_name, True)
}
)
# ELB name validation
load_balancer_name = Parameter(
"LoadBalancerName",
Type="String",
Description="Load balancer name",
props={
"LoadBalancerName": (elb_name, True)
}
)from troposphere.validators import integer_range
from troposphere import Parameter
# Create range validator
port_range_validator = integer_range(1024, 65535)
# Parameter with range validation
app_port = Parameter(
"ApplicationPort",
Type="Number",
Default=8080,
Description="Application port (1024-65535)",
props={
"ApplicationPort": (port_range_validator, True)
}
)
# Database storage size validation
storage_range_validator = integer_range(20, 1000)
db_storage = Parameter(
"DBStorageSize",
Type="Number",
Default=100,
Description="Database storage size in GB (20-1000)",
props={
"DBStorageSize": (storage_range_validator, True)
}
)from troposphere import AWSProperty
from troposphere.validators import mutually_exclusive, exactly_one
class CustomProperty(AWSProperty):
props = {
"SourceType": (str, False),
"SourceValue": (str, False),
"SourceArn": (str, False),
"SourceAccount": (str, False)
}
def validate(self):
# Exactly one source must be specified
exactly_one(
self.__class__.__name__,
self.properties,
["SourceValue", "SourceArn", "SourceAccount"]
)
# Source type and value are mutually exclusive with ARN
mutually_exclusive(
self.__class__.__name__,
self.properties,
[["SourceType", "SourceValue"], ["SourceArn"]]
)import re
import ipaddress
from troposphere import Parameter
from troposphere.validators import one_of
def validate_cidr_block(cidr):
"""Validate CIDR block format."""
try:
ipaddress.IPv4Network(cidr, strict=False)
return cidr
except ValueError:
raise ValueError(f"Invalid CIDR block: {cidr}")
def validate_email(email):
"""Validate email address format."""
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
if not re.match(pattern, email):
raise ValueError(f"Invalid email address: {email}")
return email
def validate_instance_family(instance_type):
"""Validate instance type belongs to allowed families."""
allowed_families = ['t2', 't3', 'm5', 'c5', 'r5']
family = instance_type.split('.')[0]
if family not in allowed_families:
raise ValueError(f"Instance family {family} not allowed")
return instance_type
# Use custom validators in parameters
vpc_cidr = Parameter(
"VpcCidr",
Type="String",
Default="10.0.0.0/16",
Description="VPC CIDR block",
props={
"VpcCidr": (validate_cidr_block, True)
}
)
notification_email = Parameter(
"NotificationEmail",
Type="String",
Description="Email for notifications",
props={
"NotificationEmail": (validate_email, True)
}
)
instance_type = Parameter(
"InstanceType",
Type="String",
Default="t3.micro",
Description="EC2 instance type (restricted families)",
props={
"InstanceType": (validate_instance_family, True)
}
)from troposphere import AWSObject
from troposphere.validators import check_required
class ConditionalResource(AWSObject):
props = {
"Type": (str, True),
"DatabaseConfig": (dict, False),
"CacheConfig": (dict, False),
"DatabaseEndpoint": (str, False),
"CacheEndpoint": (str, False)
}
def validate(self):
# Check conditional requirements based on type
if self.properties.get("Type") == "database":
check_required(
self.__class__.__name__,
self.properties,
["DatabaseConfig", "DatabaseEndpoint"]
)
elif self.properties.get("Type") == "cache":
check_required(
self.__class__.__name__,
self.properties,
["CacheConfig", "CacheEndpoint"]
)from troposphere import Template, Parameter
from troposphere.validators import positive_integer
def safe_create_parameter(template, name, param_type, default_value, validator=None):
"""Safely create parameter with validation and error handling."""
try:
props = {}
if validator:
props[name] = (validator, True)
param = Parameter(
name,
Type=param_type,
Default=default_value,
props=props
)
return template.add_parameter(param)
except ValueError as e:
print(f"Validation error for parameter {name}: {e}")
raise
except Exception as e:
print(f"Error creating parameter {name}: {e}")
raise
# Usage
template = Template()
# This will succeed
memory_param = safe_create_parameter(
template, "MemorySize", "Number", 128, positive_integer
)
# This would fail validation
try:
invalid_param = safe_create_parameter(
template, "InvalidMemory", "Number", -1, positive_integer
)
except ValueError:
print("Caught validation error for negative memory size")Install with Tessl CLI
npx tessl i tessl/pypi-troposphere