CloudFormation¶
CloudFormation turns a template into service resources and gives clients one place to create, inspect, update, and delete them. Roto implements this as a useful subset: stack operations are synchronous, and templates call the same service handlers used by direct SQS, SNS, S3, DynamoDB, Kinesis, IAM, and Lambda requests.
Run the end-to-end demo¶
Start a seeded local server in one terminal, then run the AWS CLI demo in another. The CLI uses fake credentials and targets Roto on port 5071.
The demo creates an SQS queue and an SNS topic subscribed to it, plus a nested
stack that creates a second queue. It then creates a consumer stack that imports
an export from the first stack. The templates show parameters, references,
outputs, and Fn::Sub. Set ROTO_ENDPOINT if the server uses another port.
The script uploads the nested template into a local S3 bucket because
AWS::CloudFormation::Stack loads its template through TemplateURL. It prints
both stacks' outputs and the nested stack's parent/root IDs. Delete the consumer
stack first so it releases its import, then delete the main stack; that removes
the nested stack and its queue. The shared template bucket remains until the
ephemeral server is restarted.
Supported resources¶
Roto supports synchronous CloudFormation stacks for nine resource types:
| Resource | Supported properties |
|---|---|
AWS::SQS::Queue |
Name, tags, visibility/retention/delay settings, long polling, FIFO/deduplication, redrive and encryption attributes |
AWS::SNS::Topic |
Name, tags, display name, FIFO/deduplication, KMS key attribute, inline subscriptions |
AWS::S3::Bucket |
Name, tags, encryption, versioning, public access block configuration |
AWS::DynamoDB::Table |
Name, tags, keys/attribute definitions, billing/throughput, GSI/LSI, stream/SSE metadata, table class, deletion protection, TTL, point-in-time recovery configuration |
AWS::Kinesis::Stream |
Name, shard count, retention, tags, stream mode, stored encryption settings |
AWS::IAM::Role |
Name, path, trust policy, description, session duration, tags, managed policy ARN attachments |
AWS::IAM::Policy |
Inline policy document attached to one or more roles |
AWS::Lambda::Function |
Creation/deletion with name, inline or S3 code, role, runtime, handler, description, timeout, memory, environment, architectures, ephemeral storage, tracing, and tags; configuration updates are unsupported |
AWS::CloudFormation::Stack |
Nested stacks loaded from an S3 TemplateURL, with parameter and tag propagation, recursive create/update/delete, and child outputs |
Resources use the existing service handlers and storage. IAM role policy documents and managed-policy attachments are stored as configuration; they do not grant or deny requests because IAM policy evaluation is not implemented.
Stack APIs¶
CreateStack, UpdateStack, DeleteStack, DescribeStacks, ListStacks,
ListExports,
ListStackResources, DescribeStackResources, DescribeStackEvents, and
GetTemplate are implemented. Change-set APIs include CreateChangeSet,
DescribeChangeSet, ExecuteChangeSet, DeleteChangeSet, and ListChangeSets.
SetStackPolicy, GetStackPolicy, and UpdateTerminationProtection are also
implemented. Successful
mutations finish before the API responds, with CREATE_COMPLETE,
UPDATE_COMPLETE, or DELETE_COMPLETE status.
Stack state is scoped by account and region and persists in cloudformation.db.
Resources and outputs remain available after server restart. Resource descriptions
include logical IDs, physical IDs, types, status, and timestamps. Stack name and
stack ARN can be used for lookups. A deleted stack can be described by ARN until
its name is reused; name lookup and listing omit deleted stacks.
Known IAM access keys and STS session keys select their owning account. For local
multi-account work without provisioning IAM credentials, an otherwise unknown
12-digit AWS_ACCESS_KEY_ID selects that account directly. For example, these
commands list separate CloudFormation namespaces:
AWS_ACCESS_KEY_ID=111122223333 AWS_SECRET_ACCESS_KEY=demo \
aws --endpoint-url http://localhost:5071 cloudformation list-stacks
AWS_ACCESS_KEY_ID=444455556666 AWS_SECRET_ACCESS_KEY=demo \
aws --endpoint-url http://localhost:5071 cloudformation list-stacks
Non-numeric or other unknown credentials use --account-id / ROTO_ACCOUNT_ID
(default 123456789012). Region still comes from the SigV4 credential scope.
Templates use inline TemplateBody. JSON and ordinary YAML mappings are accepted;
YAML short tags !Ref, !GetAtt, !Sub, and !ImportValue are accepted. Available template features are:
Resources,Outputs,Description,Metadata, andConditions.- String
Parameters, defaults, explicit values, andUsePreviousValueon updates. Ref,Fn::GetAtt(list or dotted-string form),Fn::Sub, including variable maps and literal escapes,Fn::Join, andFn::ImportValue.Fn::Ifwith named conditions usingFn::Equals,Fn::And,Fn::Or,Fn::Not, and condition references. Cyclic condition references are rejected. Resource/outputConditionfields are unsupported.AWS::NoValueomits a property or list item, including when selected byFn::If.DeletionPolicyandUpdateReplacePolicywithDeleteorRetain.AWS::AccountId,AWS::Region,AWS::StackName,AWS::StackId,AWS::Partition, andAWS::URLSuffixreferences.- Explicit
DependsOnand implicit dependencies from references. Forward references are ordered before resource creation; missing dependencies and cycles are rejected. - Stack tags merged into resource tags; a resource tag takes precedence when both use the same key.
- Nested
AWS::CloudFormation::Stackresources loadTemplateURLobjects from Roto's S3 service. Nested parameters, tags, outputs, and parent/root stack IDs are tracked, and nested stacks can themselves contain nested stacks.
Export names are returned in stack descriptions and ListExports. Fn::ImportValue
resolves exports from another stack in the same account and region. Export names
must be unique there, and an export cannot be changed or removed while another
stack imports it. The exporter also cannot be deleted while an import is active;
delete the importing stack first. ListExports returns pages of up to 100
exports.
For example, an exporting stack can publish a resource attribute:
Outputs:
QueueArn:
Value:
Fn::GetAtt: [Queue, Arn]
Export:
Name:
Fn::Sub: "${AWS::StackName}-QueueArn"
A separate stack can accept the export name as a parameter and import it:
Parameters:
QueueArnExportName:
Type: String
Outputs:
SharedQueueArn:
Value:
Fn::ImportValue:
Ref: QueueArnExportName
The export name is resolved when the first stack is created. The importing
stack's output then contains the exported value; inspect available names with
aws --endpoint-url http://localhost:5071 cloudformation list-exports.
Nested stacks¶
An AWS::CloudFormation::Stack resource creates a child stack from a template
object in Roto's S3 service. Its Parameters and Tags are passed to the child.
The parent can read child outputs through Fn::GetAtt, using attributes such as
Outputs.QueueUrl. Ref on the nested resource returns the child stack ID.
Child records expose ParentId and RootId, and nested stacks can contain more
nested stacks up to a maximum depth of 16.
Creating or updating the parent creates or updates the child in the same account and region. Deleting the parent recursively deletes the child. A child deletion failure leaves the failed child and remaining resources visible so the delete can be retried. Nested change sets are not supported.
Change sets and protection¶
Create/update change sets store a target template, parameters, tags, and a resource
change preview. DescribeChangeSet reports additions, removals, modifications,
and replacement estimates; unresolved properties can produce a conditional
replacement estimate. A change set with no resource changes is marked FAILED
and cannot be executed. Executing a create change set creates its
REVIEW_IN_PROGRESS stack; executing an update uses the stack update path.
Stack policies support Allow and Deny statements with Action and Resource.
The current evaluator enforces matching Deny statements for Update:Modify,
Update:Replace, and Update:Delete; it does not implement full AWS policy
semantics. StackPolicyDuringUpdateBody can override the policy for an update.
Termination protection blocks DeleteStack until disabled.
DescribeStackEvents returns persisted events newest first in pages of 100;
ListExports returns pages of up to 100. Other stack/resource/change-set listing
APIs do not paginate. GetTemplate returns the stored stack template as JSON;
fetching a change-set template is unsupported.
Template specification checks¶
On the first template check, Roto loads pinned resource specification version
267.0.0 from its cache or downloads it with a ten-second timeout. Cached and
downloaded bytes are checked against a pinned SHA-256. The specification checks
top-level property names, required properties, and scalar types; Roto's built-in
checks still determine which resource types and properties it can execute.
Set ROTO_CFN_SPEC=offline to skip downloading. ROTO_CACHE_DIR chooses the cache
directory, which defaults to ~/.cache/roto. If the specification is unavailable,
Roto logs a warning and uses its built-in template checks.
Example¶
Create a queue and an SNS topic subscribed to it:
import boto3
import json
cf = boto3.client(
"cloudformation",
endpoint_url="http://localhost:5070",
region_name="us-east-1",
aws_access_key_id="testing",
aws_secret_access_key="testing",
)
template = {
"Resources": {
"Queue": {"Type": "AWS::SQS::Queue"},
"Topic": {
"Type": "AWS::SNS::Topic",
"Properties": {
"Subscription": [
{"Protocol": "sqs", "Endpoint": {"Fn::GetAtt": ["Queue", "Arn"]}}
]
},
},
},
"Outputs": {
"QueueURL": {"Value": {"Ref": "Queue"}},
"TopicARN": {"Value": {"Ref": "Topic"}},
},
}
stack_id = cf.create_stack(
StackName="notifications",
TemplateBody=json.dumps(template),
)["StackId"]
print(cf.describe_stacks(StackName=stack_id)["Stacks"][0]["Outputs"])
Resources without explicit names receive generated names. Ref returns the queue
URL, topic ARN, bucket name, or table name, depending on resource type. SQS physical
IDs use the AWS queue URL format; SDK clients still need roto's endpoint URL.
Updates and failures¶
Updates can add/remove resources, change supported properties in place, and replace resources when a name, FIFO mode, or DynamoDB key/index definition changes. Replacement creates the new resource before deleting the old one. A named resource replacement needs a different explicit name. DynamoDB data is preserved for in-place updates and is removed with its table on replacement or deletion. Changing an SNS inline subscription reconciles subscriptions owned by the stack.
Unsupported resource types, properties, template sections, and intrinsic functions fail explicitly. IAM users, groups, and managed policy resources, EventBridge and SSM resources, transforms, nested change sets, imports of existing resources, top-level template URLs, snapshot retention policies, and rollback triggers remain unsupported. Some property removals also fail when the underlying service needs an update that this subset does not support. Lambda configuration updates are unsupported; creating a function does not attach a local executor automatically.
Resource-operation failures are recorded in stack status and reason; the API
call can succeed even when the resulting stack fails. Create failures default to
rolling back created resources. OnFailure can select ROLLBACK, DELETE, or
DO_NOTHING; DisableRollback=true selects DO_NOTHING and cannot be combined
with OnFailure. Update failures attempt to reconcile the previous template,
parameters, and tags, resulting in UPDATE_ROLLBACK_COMPLETE or
UPDATE_ROLLBACK_FAILED. This is a reconciliation attempt, not a transactional
restore of deleted resource data. ExecuteChangeSet also accepts
DisableRollback.
A process interruption leaves its last saved progress visible. Delete failures
retain remaining resources and can be retried after fixing the cause, such as
emptying a nonempty S3 bucket. RetainResources leaves named logical resources
in place while dropping them from the stack. DeletionMode=FORCE_DELETE_STACK
also drops resources whose deletion fails. Template retention policies apply to
deletion and replacement.
The implementation is checked against 25 existing Moto integration tests across SQS, SNS, S3, DynamoDB, and Kinesis. The runner corrects invalid schemas and overly strict optional-field assertions in Moto's old DynamoDB CloudFormation fixtures in a temporary copy; vendored tests remain unchanged and DynamoDB validation stays strict. Additional SDK checks cover wiring, index queries, updates, restart, account/region isolation, failure state, cleanup, and reset: