Skip to main content

Templates

Starting from an empty definition file means knowing which modules exist, which inputs each one takes, and which order they have to run in. A template is a working stack that already answers all three, so the first thing you do is change something rather than assemble something.

There are two sets, and they are for different moments.

Scaffold templates​

npx @quadnix/octo-build create-app -t <template> -n <app-name>

Each one writes a complete project: a definition file with its modules wired together, a package.json with the four lifecycle scripts, and any supporting files the stack needs. Then it runs npm install. What you get after that is the lifecycle.

The -t flag changes only octo.yaml and, for the website template, a folder of files to upload. The rest of the project is identical whichever you choose.

aws-s3-website​

The smallest one. No VPC, and it applies in seconds.

octo.yaml
modules:
- moduleId: app-module
moduleClass: SimpleAppModule
moduleInputs:
name: aws-s3-website

- moduleId: account-module
moduleClass: AwsIniAccountModule
moduleInputs:
accountId: "${env.AWS_ACCOUNT_ID}"
app: "${{app-module.model.app}}"

- moduleId: region-module
moduleClass: AwsServerlessRegionModule
moduleInputs:
account: "${{account-module.model.account}}"
name: aws-s3-website
regionId: aws-us-east-1

- moduleId: s3-website-service-module
moduleClass: AwsS3StaticWebsiteServiceModule
moduleInputs:
bucketName: "${env.AWS_S3_WEBSITE_BUCKET_NAME}"
directoryPath: website
region: "${{region-module.model.region}}"

Four modules, and one of them produces the whole website: a bucket, its website configuration, a public access block, a bucket policy, and one object per file in website/. The region before it creates nothing; it says which AWS region the bucket goes in. Static Website runs this one end to end, from scaffold to a URL you can open.

aws-ecs-server​

A container running on ECS behind an application load balancer, and the one worth reading even if you never deploy it, because it is composition at a realistic size. Eleven modules:

octo.yaml (abridged)
modules:
- moduleId: app-module # SimpleAppModule
- moduleId: account-module # AwsIniAccountModule
- moduleId: region-module # AwsMultiAzRegionModule, two AZs
- moduleId: backend-server-module # AwsServerModule
- moduleId: backend-deployment-v1-module # AwsDeploymentModule, an echo-server image
- moduleId: qa-environment-module # AwsEcsEnvironmentModule
- moduleId: public-subnet-module # AwsSimpleSubnetModule, with a NAT gateway
- moduleId: public-backup-subnet-module # AwsSimpleSubnetModule, in the second AZ
- moduleId: private-subnet-module # AwsSimpleSubnetModule, NAT-attached
- moduleId: backend-v1-qa-execution-module # AwsEcsExecutionModule
- moduleId: qa-api-alb-module # AwsEcsAlbServiceModule

Read it for the wiring rather than the resources. The private subnet is given the two public subnets as siblings, one of them marked attachToNatGateway: true. The execution is given a deployment, an environment, and a subnet, which is exactly what an execution is. The load balancer is given the two public subnets and the execution it should route to. Every one of those is a node handed to a module, not an ID copied between files.

This template creates a NAT gateway, which costs money by the hour. Tear it down when you are done.

aws-dynamodb-service​

A DynamoDB table with a hash and range key, one global secondary index, one local secondary index, streams, and a TTL attribute. Four modules, and the interesting part is the comments in the generated definition file, which mark which of those can change on a live table and which recreate it.

The reference suites​

@quadnix/octo-templates in the Octo repository holds the three stacks above as end-to-end test suites, and three more that have no scaffold. They are not something you install; they are where the templates are actually verified, and they run the real lifecycle against a real AWS account.

They are worth reading for the things a definition file cannot show. aws-ecs-server has a drift suite that changes infrastructure out from under Octo and checks that validate reports it. It has an incremental suite that applies a stack, changes one input, and applies again, which is the case a template on its own never exercises. The three above build their modules through a ModuleDefinitions class that a test can mutate, so "remove this module and run again" is a line of code.

The three without a scaffold all run the octo binary rather than driving Octo in process, because each holds an external resource: Terraform shells back out to a second octo process mid-apply, which an in-process test never sees.

aws-ecr-image publishes container images into ECR and needs Docker as well as an AWS account. It carries a second definition file alongside its own, octo-ecs.yaml, where a version and an ECS service running that version are declared together and created by one apply.

aws-cloudfront-website serves a private bucket through a CloudFront distribution, and its external resource is the invalidation that drops what the edge locations are holding when the published files change. It is the slowest suite here — a distribution takes minutes to create and longer to delete — so it asks its questions of one live distribution rather than creating several: a re-apply that has to change nothing, a content change that has to invalidate exactly once, and a price class edit that has to come back with the same distribution id.

aws-lambda-api publishes one image and runs it on Lambda as three functions, in a serverless region, two of them behind a CloudFront distribution. It needs Docker, and builds the image for arm64, the processor the functions are declared to run on. It calls a function's URL with a signed request, calls it again through the distribution, and changes an environment variable to see the function answer differently. One function streams a response past Lambda's 6 MB buffered limit. Another is not behind the distribution and its URL admits no one, so the only way in is the IAM statements the first function's server grants: the suite calls through them, then takes the invoke away and sees the call refused. Its servers and deployments create nothing; each execution creates its function, its URL, and the role it runs as.

If you are writing modules of your own, these are the closest thing to a worked example of testing them against something real. Testing a Module is the page for that.

Your own​

A template is a definition file plus whatever it needs, so a starting point of your own is a directory somebody copies. That is fine, and for most teams it is the right amount of machinery.

The version that scales better is a module. A template says "here is a stack, change the names"; a module says "here is what a region means here, take it". If every project at your company begins by copying the same six modules and changing the same four inputs, those six modules are one module nobody has written yet. Create a Module covers writing it, and Publishing a Library covers handing it to other teams.