AWS CloudFormation creation library that facilitates building infrastructure templates programmatically in Python
Additional utilities for user data, template conversion, and stack monitoring to enhance CloudFormation workflow integration.
Utilities for loading and processing EC2 user data scripts from files.
def from_file(filepath: str) -> str:
"""
Load user data from file.
Args:
filepath: Path to user data script file
Returns:
str: File contents as string
Raises:
IOError: If file cannot be read
"""
def from_file_sub(filepath: str) -> str:
"""
Load user data from file with variable substitution support.
Args:
filepath: Path to user data script file with ${variable} placeholders
Returns:
str: File contents with substitution placeholders preserved
Raises:
IOError: If file cannot be read
"""Convert existing CloudFormation JSON/YAML templates to Troposphere Template objects.
class TemplateGenerator:
def __init__(self):
"""Template conversion utility."""
def convert_template(self, template_data: dict) -> Template:
"""
Convert CloudFormation template dictionary to Troposphere Template.
Args:
template_data: CloudFormation template as dictionary
Returns:
Template: Troposphere Template object
Raises:
ValueError: If template format is invalid
"""
def convert_from_file(self, filepath: str) -> Template:
"""
Convert CloudFormation template file to Troposphere Template.
Args:
filepath: Path to CloudFormation template file (JSON or YAML)
Returns:
Template: Troposphere Template object
Raises:
IOError: If file cannot be read
ValueError: If template format is invalid
"""Utilities for monitoring CloudFormation stack events and status.
def get_events(stackname: str, region: str = None, aws_profile: str = None) -> List[dict]:
"""
Get CloudFormation stack events.
Args:
stackname: CloudFormation stack name
region: AWS region (uses default if not specified)
aws_profile: AWS profile name (uses default if not specified)
Returns:
List[dict]: Stack events
Raises:
ClientError: If stack not found or access denied
"""
def tail(stackname: str, region: str = None, aws_profile: str = None) -> None:
"""
Monitor CloudFormation stack events in real-time.
Args:
stackname: CloudFormation stack name
region: AWS region (uses default if not specified)
aws_profile: AWS profile name (uses default if not specified)
Raises:
ClientError: If stack not found or access denied
KeyboardInterrupt: If monitoring interrupted by user
"""Utilities for encoding and data transformation.
def encode_to_dict(obj) -> Union[Dict[str, Any], List[Any], Any]:
"""
Recursively encode objects to dictionary format.
Args:
obj: Object to encode (BaseAWSObject, list, dict, or primitive)
Returns:
Encoded object in dictionary format
"""
def depends_on_helper(obj) -> Union[str, List[str], Any]:
"""
Helper for processing DependsOn values.
Args:
obj: Dependency object (AWSObject, string, or list)
Returns:
Processed dependency (resource title or list of titles)
"""from troposphere import Template, Base64
from troposphere.ec2 import Instance
from troposphere.helpers.userdata import from_file, from_file_sub
template = Template()
# Load static user data from file
user_data_script = from_file("/path/to/userdata.sh")
instance = template.add_resource(Instance(
"WebServer",
ImageId="ami-0abcdef1234567890",
InstanceType="t3.micro",
UserData=Base64(user_data_script)
))
# Load user data with substitution placeholders
user_data_template = from_file_sub("/path/to/userdata_template.sh")
# Use with Sub function for variable substitution
from troposphere import Sub, Ref
instance_with_vars = template.add_resource(Instance(
"AppServer",
ImageId="ami-0abcdef1234567890",
InstanceType="t3.micro",
UserData=Base64(Sub(user_data_template, {
"StackName": Ref("AWS::StackName"),
"Region": Ref("AWS::Region")
}))
))Example userdata_template.sh:
#!/bin/bash
echo "Stack: ${StackName}" >> /var/log/stack-info.log
echo "Region: ${Region}" >> /var/log/stack-info.log
echo "Starting application..." >> /var/log/stack-info.log
# Install application
curl -o /tmp/app.zip https://releases.example.com/app.zip
unzip /tmp/app.zip -d /opt/app/
chmod +x /opt/app/start.sh
/opt/app/start.shfrom troposphere.template_generator import TemplateGenerator
import json
# Convert existing CloudFormation template
generator = TemplateGenerator()
# From file
troposphere_template = generator.convert_from_file("existing-stack.json")
# From dictionary
with open("cloudformation-template.json", "r") as f:
cf_template = json.load(f)
troposphere_template = generator.convert_template(cf_template)
# Now you can modify using Troposphere
from troposphere.ec2 import Instance
new_instance = troposphere_template.add_resource(Instance(
"AdditionalInstance",
ImageId="ami-0abcdef1234567890",
InstanceType="t3.micro"
))
# Convert back to CloudFormation
updated_cf_json = troposphere_template.to_json()
updated_cf_yaml = troposphere_template.to_yaml()from troposphere.utils import get_events, tail
import time
# Get current stack events
try:
events = get_events("my-stack", region="us-east-1")
print("Recent stack events:")
for event in events[:10]: # Show last 10 events
print(f"{event['Timestamp']}: {event['LogicalResourceId']} - {event['ResourceStatus']}")
except Exception as e:
print(f"Error getting stack events: {e}")
# Monitor stack in real-time (blocks until interrupted)
print("Monitoring stack events (Ctrl+C to stop)...")
try:
tail("my-stack", region="us-east-1", aws_profile="production")
except KeyboardInterrupt:
print("Monitoring stopped")from troposphere import encode_to_dict, Template, Ref
from troposphere.ec2 import Instance
template = Template()
instance = template.add_resource(Instance(
"MyInstance",
ImageId="ami-0abcdef1234567890",
InstanceType="t3.micro"
))
# Encode individual resource
instance_dict = encode_to_dict(instance)
print("Instance as dict:", instance_dict)
# Encode entire template
template_dict = encode_to_dict(template.to_dict())
print("Template as dict:", template_dict)
# Custom object encoding
class CustomObject:
def __init__(self, name, value):
self.name = name
self.value = value
def to_dict(self):
return {"Name": self.name, "Value": self.value}
custom = CustomObject("test", "value")
encoded_custom = encode_to_dict(custom)
print("Custom object:", encoded_custom)from troposphere import depends_on_helper, Template
from troposphere.ec2 import Instance, SecurityGroup
template = Template()
# Create security group
sg = template.add_resource(SecurityGroup(
"MySecurityGroup",
GroupDescription="Test security group"
))
# Create instance
instance = template.add_resource(Instance(
"MyInstance",
ImageId="ami-0abcdef1234567890",
InstanceType="t3.micro"
))
# Use depends_on_helper for processing dependencies
# These are equivalent:
instance.DependsOn = depends_on_helper(sg) # Converts to "MySecurityGroup"
instance.DependsOn = "MySecurityGroup" # Direct string
# For multiple dependencies
dependencies = [sg, "SomeOtherResource"]
instance.DependsOn = depends_on_helper(dependencies) # Converts to ["MySecurityGroup", "SomeOtherResource"]from troposphere import Template, Parameter, Sub, Base64, Ref
from troposphere.ec2 import Instance
from troposphere.helpers.userdata import from_file_sub
template = Template()
# Parameters for user data
app_version = template.add_parameter(Parameter(
"AppVersion",
Type="String",
Default="1.0.0",
Description="Application version to deploy"
))
database_url = template.add_parameter(Parameter(
"DatabaseURL",
Type="String",
Description="Database connection URL"
))
# Load user data template
user_data_template = from_file_sub("scripts/app-setup.sh")
# Create instance with parameterized user data
instance = template.add_resource(Instance(
"AppServer",
ImageId="ami-0abcdef1234567890",
InstanceType="t3.micro",
UserData=Base64(Sub(user_data_template, {
"AppVersion": Ref(app_version),
"DatabaseURL": Ref(database_url),
"StackName": Ref("AWS::StackName"),
"Region": Ref("AWS::Region")
}))
))Example app-setup.sh:
#!/bin/bash
set -e
# Log all output
exec > >(tee /var/log/user-data.log)
exec 2>&1
echo "Starting application setup..."
echo "Stack: ${StackName}"
echo "Region: ${Region}"
echo "App Version: ${AppVersion}"
# Set environment variables
export APP_VERSION="${AppVersion}"
export DATABASE_URL="${DatabaseURL}"
export STACK_NAME="${StackName}"
# Download and install application
cd /opt
wget https://releases.example.com/app-${AppVersion}.tar.gz
tar -xzf app-${AppVersion}.tar.gz
cd app-${AppVersion}
# Configure application
cat > config.json << EOF
{
"database_url": "${DatabaseURL}",
"stack_name": "${StackName}",
"region": "${Region}",
"version": "${AppVersion}"
}
EOF
# Start application
chmod +x start.sh
./start.sh
echo "Application setup completed successfully"Install with Tessl CLI
npx tessl i tessl/pypi-troposphere