Use Cases
For BYO-EKS- Create Kubernetes namespaces in an existing EKS cluster.
- Create EKS access entries for the runner IAM roles.
- Grant the Runner Subnet Security Group access to an existing EKS Cluster.
- Create a dedicated RDS Subnet in the freshly created VPC.
- Configure an AWS Transit VPC in addition to a dedicated VPC for a BYOC app.
Configuration
Custom nested stacks are configured in[[custom_nested_stacks]] blocks in stack.toml:
Properties
Execution Order
Custom nested stacks execute after the VPC and runner nested stacks. Theindex field determines the order among
custom stacks:
- VPC nested stack
- Runner ASG nested stack
- Custom nested stacks ordered by
index, ascending.
VPC and RunnerAutoScalingGroup resources. Each
subsequent stack depends on the previous one. This means stacks are provisioned sequentially, and a failure in one will
prevent later stacks from executing.
Reserved Parameters
Nuon automatically injects the following reserved parameters into your nested template if they are defined in the template’sParameters section. These are always set by Nuon and are never hoisted to the parent stack or exposed to
the customer.
Nuon Identity Parameters
If your template defines any of these as parameters, Nuon populates them automatically. If your template does not define
them, they are omitted — no error is raised.
Role Enable Parameters
Your app’s IAM role configuration (in
permissions/ and break_glass/) generates Enable* parameters in the parent
stack (e.g., EnableRunnerProvision, EnableRunnerDeprovision). These are also treated as reserved parameters in
custom nested stacks.
If your template declares one of these parameters, Nuon passes through the parent stack’s parameter value (a Ref to
the parent parameter). If your template does not declare the parameter, it is not injected.
Your nested template can then use its own Conditions block to conditionally create resources based on whether a role
is enabled:
First-Class Output Wiring
If a parameter in your custom template matches the name of an output from the VPC or runner nested stacks, Nuon automatically wires it using!GetAtt. This lets your template consume outputs from the built-in stacks without any
explicit configuration.
For example, if the VPC template outputs VPC and RunnerSubnet, and your custom template declares parameters with
those same names, they will be automatically populated:
Inter-Stack Output Wiring
Outputs from earlier custom nested stacks are automatically wired to matching parameters in later custom stacks, using the same mechanism as first-class output wiring. If stack A (index 0) declares an output calledSharedSubnetID, and
stack B (index 1) declares a parameter called SharedSubnetID, Nuon will automatically set the parameter value using
!GetAtt StackA.Outputs.SharedSubnetID.
This works across any number of stacks in the chain — stack C can consume outputs from both stack A and stack B.
Auto-wired parameters from inter-stack outputs are not hoisted to the parent stack.
Precedence: First-class outputs (from the VPC and runner stacks) always take priority over custom stack outputs. If
both the VPC stack and a custom stack produce an output with the same name, the VPC stack output is used.
Parameter Hoisting
Any non-reserved parameters defined in your nested template that are not auto-wired from first-class outputs and not explicitly mapped viaparameters are hoisted into the parent CloudFormation stack. This means:
- Parameters appear in the CloudFormation console UI when the customer creates or updates the stack.
- Parameters are grouped under a label matching the stack
name. - Default values from your template are preserved.
- Parameter types (e.g.,
AWS::EC2::VPC::Id,String,Number) are preserved.
nuon apps sync will fail with a conflict error.
Parameter Mapping
Use theparameters field to set a template parameter from your app config. This is useful for passing install-specific
values into your nested template without hoisting them to the CloudFormation UI.
- Its value is rendered when the install stack is generated.
- It is removed from the hoisted parameter set (not shown in the CloudFormation UI). Because the value is resolved before the customer applies the stack, it cannot be edited in the CloudFormation console.
- If a referenced install input has no value, it resolves to that input’s declared default (an empty string if it has none).
Templating
Parameter values are full Go templates with sprig functions available, so you can compose literals, branch on an optional input, or transform a value:Authoring Templates
Template Structure
Templates must be valid CloudFormation YAML. At minimum:Naming Conventions
Thename field in stack.toml is converted to a CamelCase CloudFormation logical ID. For example:
The logical ID must not conflict with existing resources in the parent stack (e.g.,
VPC, RunnerAutoScalingGroup).
Validation Rules
The following conditions will cause a sync error:- Missing
nameortemplate_url - Duplicate
indexvalues across custom stacks namethat produces an empty or invalid CloudFormation logical ID- Logical ID that conflicts with an existing resource in the parent stack
- Duplicate logical IDs across custom stacks
- Parameter name conflicts across stacks
Using Lambda Custom Resources
For resources that CloudFormation cannot manage natively (e.g., Kubernetes namespaces), use a Lambda-backed custom resource pattern:- Define an IAM role with the minimum permissions needed
- Create a Lambda function inline (using
ZipFile) or reference an S3 artifact - Create a
Custom::resource that invokes the Lambda
Handling Delete Events
Lambda custom resources receiveCreate, Update, and Delete request types. Always handle the Delete event, even
if it is a no-op. Failing to respond to a delete event will cause the CloudFormation stack deletion to hang.
Hosting Templates
Templates must be hosted on S3. CloudFormation requires that nested stack template URLs point to an S3 bucket — other URL types (e.g., GitHub raw URLs, arbitrary HTTPS endpoints) are not supported by CloudFormation. Upload your templates to an S3 bucket and reference them using the full S3 URL:Example: Kubernetes Namespaces
See the byo-eks example app config for a complete working example that uses custom nested stacks to create Kubernetes namespaces and EKS access entries.Permissions
The CloudFormation stack execution role must have permission to create the resources defined in your nested templates. For Lambda-backed custom resources, this includes:iam:CreateRole,iam:PutRolePolicy,iam:AttachRolePolicy,iam:DeleteRole,iam:DeleteRolePolicy,iam:DetachRolePolicylambda:CreateFunction,lambda:DeleteFunction,lambda:InvokeFunction- Any permissions the Lambda function itself needs (passed via its IAM role)
These permissions are distinct from the permissions in the
permissions/ directory in the app config. They are the
permissions the customer has at the moment they execute the CloudFormation template.