Skip to content
cdkd

Schema versions and upgrades

Every state record carries a schema version, and the current one is 11. A newer cdkd reads every older version, so upgrading cdkd needs no migration step. An older cdkd cannot read a record that a newer one has written.

# includes the schema version read from the bucket
cdkd state info

# the "version" one record was last saved with
cdkd state show MyStack --json

This page is part of State Management.

Upgrading cdkd

A newer cdkd upgrades an older record in memory when it loads the record. The next time cdkd writes the record, it saves the record in the current version. There is no migration command and nothing to run.

Mixing cdkd versions on one stack

An older cdkd refuses a record written in a version it does not know, and exits:

Unsupported state schema version 11 for stack MyStack. This cdkd binary supports versions 1, 2, 3, 4, 5, 6, 7, 8, 9, 10. Upgrade cdkd to a version that supports schema 11.

cdkd writes the current version on every record it saves. So once a newer cdkd has written a stack's record, every older cdkd fails on that stack. Upgrade every machine and CI job that deploys a stack at the same time.

What each version added

Version What it added
1 The original layout, with no region in the key; still readable
2 The region in the key
3 observedProperties, the drift baseline
4 imports
5 deletionPolicy, updateReplacePolicy
6 The nested-stack parent fields
7 provisionedBy
8 outputReads
9 exportNames
10 observedBaselineRefused
11 NoEcho values stored as ***

What a state record contains says what each field means. The contributor page State schema internals has the full history of each version.

The first deploy after an upgrade

The first deploy of a stack after an upgrade can do a little extra work, depending on how old the record is.

From before version 3. The deploy records observedProperties for the existing resources in the background. cdkd drift needs that field as its baseline. Pass --no-capture-observed-state to skip the capture. To record the field without a deploy, run cdkd state refresh-observed MyStack.

From before version 5. The deploy reports an UPDATE for every resource whose template carries a DeletionPolicy or an UpdateReplacePolicy. The deploy only writes the attribute into the record. It makes no AWS call for these updates.

From before version 11. The deploy replaces NoEcho values in the record with ***. Secrets in state describes what to rotate afterwards.

provisionedBy: which route owns a resource

cdkd creates a resource through one of two routes: its own SDK provider for the type, or the Cloud Control API as a fallback. Provisioning Layers explains the two. Each resource entry records the route that created it in provisionedBy, as sdk or cc-api. A custom resource is recorded as sdk.

cdkd state show prints the field for each resource as ProvisionedBy: sdk or ProvisionedBy: cc-api.

Two commands read it. cdkd destroy uses it to choose how to delete the resource, and cdkd drift uses it to choose how to read the resource.

A recorded cc-api keeps the resource on Cloud Control for later deploys. This holds even after cdkd gains an SDK provider for the type, because switching routes could change the physical ID.

Edge cases

A record with no provisionedBy. A record written before version 7 has no such field, and cdkd state show prints ProvisionedBy: (sdk, legacy default) for it. The resource is not tied to a route, so cdkd chooses the route again on the next deploy.

A resource that returns to the SDK provider. A few types move from Cloud Control back to the SDK provider without being asked. cdkd allows this only for a type whose SDK provider addresses the resource by the same physical ID, so the move replaces nothing. cdkd diff marks such a resource [returning to SDK provider]. Pass --pin-cc-api <logicalId> to decline the move for that deploy.

A type Cloud Control cannot manage correctly. Such a type moves to the SDK provider whether or not you pass --pin-cc-api.

The contributor page State schema internals lists the types and the conditions.

exportNames: which outputs are exports

outputs holds plain output names and export names side by side. exportNames lists the keys that are exports, and only those keys can satisfy an Fn::ImportValue in another stack.

exportNames Meaning
A list of names Only these keys can be imported
[] The stack exports nothing
Absent A record from before version 9

In a record from before version 9, every output key can be imported until the stack is next deployed. That deploy writes the field, even when the template did not change.

Two stacks export the same name

The stack deployed most recently wins, and cdkd warns. CloudFormation refuses the second stack in this situation, so rename one of the exports.

An output the deploy could not resolve

When a deploy cannot resolve an output, it skips that output. It stores no value for the output and adds the output's name to the record's skippedOutputs list. One cause is a secret reference that names a JSON key the secret does not contain.

The list exists for cdkd diff. Diff does not resolve secrets, so it cannot tell that the output would fail again. Without the list, every diff of the unchanged stack would preview an ADD for the output, and cdkd diff --fail would keep failing.

With the list, diff shows no row for the output until one of two things happens: the template inputs that the output reads change, or a resource the output refers to is itself changing in that diff.

How the output comes back depends on where you fix it:

  • In the template. The row returns in cdkd diff, and the next deploy publishes the output.
  • Outside the template, for example by adding the missing key to the secret. Diff cannot see this fix. The next deploy resolves the output and removes its name from the list.

Last updated: