cdkd deploy: checks on the cloud assembly
The cloud assembly is the directory that synthesis writes, usually cdk.out.
It holds the templates, the asset manifests and the files to upload.
cdkd deploy checks its nested templates and file paths right after
synthesis, before it expands a macro, publishes an asset, takes a lock or
creates a resource.
An assembly that the CDK generated passes every check on this page. The checks
catch an assembly that was edited by hand or produced by another tool. One
malformed stack stops the whole run with exit code 1, and that includes
--dry-run.
cdkd deploy MyStack # synthesizes, checks, then deploys
cdkd deploy -a cdk.out MyStack # checks an assembly that already exists
cdkd deploy -a cdk.out 'MyStage/*' # a Stage's stacks, from the app's cdk.out
This page is part of cdkd deploy: safety & compatibility flags.
Cyclic nested templates are refused
cdkd refuses an assembly in which a nested stack's template includes itself, directly or through other nested stacks:
SynthesisError: The nested template tree under stack Parent contains a
cycle: Child (/path/to/cdk.out/child.json) then Loop
(/path/to/cdk.out/child.json). Nested stack Loop (declared in stack
Parent~Child) resolves to a template that is already on that nesting chain,
so its Metadata['aws:asset:path'] closes a cycle. CDK emits an acyclic nested
template tree with relative asset paths, so this indicates the synth output was
hand-modified or generated by a non-CDK toolchain. Refusing to start the
deploy; nothing has been published or provisioned.
A parent template points at each nested stack's template through the nested
stack resource's Metadata['aws:asset:path']. cdkd follows those paths from
every stack it is about to deploy, dependencies included. It refuses when a
path leads to a template that is already on the same chain from parent to
child.
Edge cases
- Two nested stacks that name the same template are a shared child, and that is allowed. Only a repeat along one chain from the root to a child is refused.
- A
Conditionon the nested stack is not evaluated. A template that includes itself behind a condition is refused too, as it is bycdkd diff --recursive. - An absolute
aws:asset:pathis refused, and the message sayswhich is absolute. The CDK always writes nested templates into the output directory. - A very large tree is refused: more than 512 levels deep, or more than 10,000 nested stacks.
- A stack you are not deploying is not checked.
Paths that leave the assembly directory
cdkd refuses a path in the assembly that points outside the assembly. For a
nested template, that is an aws:asset:path that resolves outside the
directory its template sits in:
SynthesisError: The nested template tree under stack Parent has nested stack
Child (reached through Child (/path/to/cdk.out/child.json)) with
Metadata['aws:asset:path']=../../etc/passwd which resolves to
/etc/passwd, outside /path/to/cdk.out. CDK emits assembly paths that stay
inside the assembly directory; one that leaves it indicates the synth output
was hand-modified or generated by a non-CDK toolchain. Refusing to start the
deploy; nothing has been published or provisioned.
A path that leaves the directory only through a symbolic link is refused the same way, and the message names the link's target.
cdkd holds every other path the assembly supplies to the same rule, because it would otherwise act on the file:
| Path in the assembly | What cdkd does with the file |
|---|---|
A nested assembly's directoryName |
Reads that directory's manifest.json |
A stack's templateFile |
Deploys whatever parses as its template |
An asset manifest's file |
Reads the list of assets to publish |
A file asset's source.path |
Zips it and uploads it to the bucket the manifest names |
A Docker asset's source.directory |
Sends it to the image build as the context |
| A stack's metadata side file | Reads its annotations |
A stack name, under cdkd synth --verbose |
Writes <name>.template.json there |
Asset source paths
The two asset rows, source.path and source.directory, are measured against
the app's output directory (cdk.out). That is because a Stage's asset
manifest legitimately points up to ../asset.<hash>. cdkd treats a relative
path and an absolute path differently:
| The asset source path is | What cdkd does |
|---|---|
| Absolute, inside the output directory | Accepts it; every staged asset is there |
| Absolute, outside the output directory | Accepts it with a warning |
| The output directory itself, by any spelling | Accepts it with a warning |
| Relative, escaping the output directory | Refuses |
The warning for an outside path names the directory and the destination. When
the path names the output directory itself, the whole assembly becomes the
asset. No cdk synth emits that.
Warning
An absolute asset path outside the output directory is uploaded. cdkd accepts it because
cdk synth --no-stagingproduces such paths, and the warning is the only protection. An assembly you did not synthesize can name any directory your user can read and upload it to a bucket the same manifest names, with your credentials.
Pointing -a at a Stage's directory
Pointing -a at a Stage's sub-assembly
(cdkd deploy -a cdk.out/assembly-MyStage) makes cdkd refuse that Stage's own
../asset.<hash>, because the asset is outside the directory you named. Point
-a at the app's cdk.out and select the stack by its display path instead:
cdkd deploy -a cdk.out 'MyStage/*'
The same rule in cdkd local
The cdkd local commands that mount Lambda code (invoke, start-api,
start-alb, start-cloudfront) apply the same rule to aws:asset:path.
They refuse a relative path that escapes and accept an absolute one with the
warning. Local Execution lists which other paths are
covered.
A pre-synthesized assembly is trusted input
Outside the path rules above, cdkd uses an asset manifest as it is written, as the CDK CLI does. So an assembly can make cdkd run a command, read a file or write a file on your machine.
Important
Deploying a pre-synthesized assembly can run code from it. A Docker asset may declare
source.executableinstead of a Dockerfile. Pointing-aat an assembly you did not produce is the same decision as running someone else's build output. Synthesize it yourself, or read it first.
cdkd warns about four kinds of manifest value that reach outside the assembly.
A command cdkd runs: source.executable
cdkd runs the value on your machine as a command line. It warns on every
command that builds a Docker asset and names the command. Every cdkd local
command that builds such an asset runs the executable too, and prints the
warning first. cdkd announces each distinct command once per run, and repeats
go to --verbose.
Host paths the image build reads
These are dockerFile, dockerBuildContexts, dockerBuildSecrets,
dockerBuildSsh, cacheFrom and cacheTo. cdkd warns when the path is
outside the output directory. A path inside the build context usually gets no
warning.
A build secret, an SSH key or a cache directory is a host path the template never shows. Reading the template therefore does not tell you what a deploy touches.
Host paths the image build writes
A dest= in dockerOutputs or in a cache option makes the build write to
that host path. cdkd warns when the path is outside the output directory,
wherever the build context is.
Whether a cache option reads or writes follows its key. A cacheFrom that
carries a dest= is a write, and a cacheTo that carries a src= is a read.
Where assets are uploaded
cdkd uploads to dest.bucketName and to the ECR repository the manifest
names, with your credentials. It warns once per name when the name is neither
shaped like a CDK bootstrap resource nor managed by cdkd.
The check looks at the name only. A bucket named like a CDK bootstrap bucket for your account can still belong to someone else.
A Stage that failed to load stops every command
When cdkd cannot read a Stage's own manifest.json, it stops, as the AWS
CDK CLI does. That happens when the Stage was never synthesized or its
directory is gone.
Stage MyStage failed to load: ENOENT reading assembly-MyStage/manifest.json.
Every stack under it is missing from the cloud assembly, so cdkd will not act
on the app. Re-synthesize the app so the Stage is written, or point --app at a
complete cloud assembly.
cdkd stops whatever you selected, including a stack outside the Stage, and
cdkd destroy stops too. To reach a deployed stack by name without the app,
use cdkd state destroy '<stack>'. cdkd scrub exits 2
(SCRUB_STAGE_LOAD_FAILED) for a Stage that failed to load.
Any other refusal under a Stage aborts the run with the innermost Stage named
ahead of it (Stage MyStage: Stack MyStage-Api ...).
Related
- cdkd deploy: safety & compatibility flags: what each deploy refusal means
cdkd diff: the same nested-template check under--recursive- Local Execution: the path rules in
cdkd local - cdkd deploy safety internals: the exact rules of the nested-template walk and the asset path checks