Skip to content
cdkd

After an import

After cdkd import, compare the adopted state with your template and then deploy. The rest of this page covers what the import recorded and the cases where that needs your attention.

cdkd diff MyStack      # what the next deploy will change
cdkd deploy MyStack
cdkd drift MyStack     # later: has anything changed outside cdkd?

To start an import, see Importing Existing Resources.

Check the first diff

cdkd diff MyStack shows two kinds of rows after an import:

  • A property that differs between AWS and your template shows as a change. The next cdkd deploy applies it.
  • A resource you did not import shows as to create.

Read the diff before you deploy. An import changes nothing in AWS, but the first deploy after it does.

A region that uses cdkd-owned asset storage

In a region opted into cdkd-owned asset storage with cdkd bootstrap, cdkd publishes assets such as Lambda code and container images to its own storage (cdkd-assets-*), and rewrites the template's asset references to match. The resources you adopt still point at the CDK bootstrap storage (cdk-<qualifier>-assets-*).

cdkd records in state the values the resources hold today, and prints an info line with the count. Two things follow:

  • The first cdkd diff shows a change for every asset reference: Lambda Code, container image URIs, and the IAM grants CDK generates on the asset bucket.
  • The first cdkd deploy repoints the resources at cdkd's storage. Do not skip it. Until it runs, an IAM policy that grants read on the asset bucket still names the CDK bootstrap bucket, while the custom resource that s3deploy.BucketDeployment generates reads from the cdkd bucket and fails with AccessDenied.

To keep using the CDK bootstrap storage, with no change after the import, pass --use-cdk-bootstrap-assets or set context.cdkd.useCdkBootstrapAssets in cdk.json.

A stack with a failed deploy still to roll back

A failed cdkd deploy leaves a rollback journal: a file in the state bucket that lists what the deploy did, so that cdkd rollback can undo it. If you import into a stack that still has one, a later rollback must not undo a resource you have since adopted.

The import therefore marks each resource it adopts on the journal before it writes state. A later cdkd rollback then skips the journal's existing entries for those logical IDs. It warns when an entry recorded a different resource under the same logical ID. Entries that a later deploy adds are still rolled back as usual.

If the import cannot read or write the journal, it refuses and writes no state. See cdkd rollback.

An older cdkd binary that does not write these marks leaves the journal untouched, so a rollback would still act on the adopted resources. Run the import again with a current binary and --force before you roll back.

The drift baseline an import records

For each resource it adopts, cdkd reads the resource's current properties from AWS and stores a copy in state. This copy is the drift baseline: cdkd drift compares it with AWS later to find changes made outside cdkd. In state.json it is the observedProperties field.

Because the baseline is a copy of what AWS holds, it can contain a secret. cdkd keeps secrets out of it where it can, as the next section describes, and skips the baseline altogether where it cannot do that safely.

Warning

Treat an imported state.json as sensitive. cdkd puts a secret reference back by matching positions between your template and what AWS returned. Where the two cannot be matched, a decrypted value can remain in the record.

Secret references are stored as references

Suppose your template sets a property to {{resolve:secretsmanager:...}}. AWS returns that property decrypted. cdkd finds the same position in what AWS returned and writes the reference there, so the baseline holds the reference and not the secret.

Where cdkd cannot match the position, it writes the mask *** there instead. A masked position affects cdkd drift in two ways:

  • While the mask is the only difference, cdkd drift reports the position as not compared and exits 2.
  • If anything else at that position changed, cdkd drift reports drift, and --accept refuses it.

A deploy that changes nothing can clear a mask, when the resource's own secret references are enough to confirm the value. cdkd drift lists the property shapes that end up masked.

Resources that get no baseline

cdkd records no baseline for a resource when it cannot place the secret references safely. Storing what AWS returned would risk storing a decrypted secret. The import says so at --verbose, and cdkd state show prints an ObservedBaseline: REFUSED line for the resource.

For such a resource:

  • cdkd drift compares AWS against the properties recorded from your template instead. That can report drift that is not real.
  • cdkd drift --accept and --revert decline the resource.
  • The decision is stored on the resource (observedBaselineRefused), and every command that would otherwise record a baseline respects it: the refresh at the start of a deploy, cdkd state refresh-observed, cdkd drift --accept and --revert, and a later import that leaves the resource in place.
  • When an import that lists other resources leaves this one in place, it also removes a baseline an earlier run recorded for it.

There are two causes, and the stored decision names which one applies.

incomplete-resolution

The recorded properties no longer spell the template's dynamic reference, or resolution discarded part of the template (such as the untaken branch of an Fn::If an import cannot bind).

This clears when a deploy creates, updates or replaces the resource, because cdkd then rebuilds the record from your template and records a real baseline. Importing the resource again clears it too. A deploy that does not change the resource does not clear it, and neither does cdkd state refresh-observed.

unverifiable-parameter

The resource depends on a template parameter, and cdkd cannot prove that the deployed value equals the parameter's Default. The next section explains the situation and how it clears.

Parameters deployed with a non-default value

cdkd import takes no parameter values, so it records every template parameter at its Default. A CloudFormation stack may have been deployed with a different value. If cdkd then stored what AWS returned as the baseline, it could store the real value of a parameter that was meant to stay secret.

So, when a CloudFormation stack backs the import, cdkd reads that stack's deployed parameter values with DescribeStacks and compares them with the defaults. The backing stack is the --migrate-from-cloudformation source and each of its nested stacks. On any other import it is a CloudFormation stack with the cdkd stack's name. cdkd only compares the deployed values. It never records or logs them.

cdkd cannot confirm a parameter when:

  • the deployed value differs from the Default;
  • the value was deployed as a {{resolve:...}} reference over a placeholder Default;
  • the parameter is NoEcho, because CloudFormation returns **** for it.

These resources then get no baseline:

  • every resource whose properties depend on such a parameter, through Ref, Fn::Sub or a condition;
  • every resource that reads an attribute of one of those with Fn::GetAtt;
  • any resource cdkd cannot check for the dependence.

cdkd warns once per stack and names the parameters.

Important

For such a resource, state holds the template Default and not the value that was deployed. A deploy that rewrites the property sends that Default to AWS. Before you deploy, put the real value in the template: replace the parameter with the {{resolve:...}} reference, or make the value the parameter's Default. Then review cdkd diff.

How this clears

Deploying a change to the resource does not clear it. cdkd deploy binds the same Default, and an update that leaves the parameter-bound property alone does not rewrite it in AWS. A baseline recorded afterwards would hold the deployed value, so an in-place update keeps the resource without a baseline. There are two ways out:

  • A deploy replaces the resource, or deletes and creates it again. The new resource is built from the properties cdkd sent, so it gets a normal baseline.
  • A later cdkd import adopts the resource again while a CloudFormation stack can prove the parameter was deployed at its Default. After --migrate-from-cloudformation that stack is gone, so importing the same physical id again keeps the resource without a baseline.

Editing the template so that nothing references the parameter does not help. An import clears this only by proving the parameter's value.

Edge cases

  • No cloudformation:DescribeStacks permission. The import still succeeds. It warns and treats every parameter as unconfirmed.
  • Nothing references a declared parameter. cdkd makes no DescribeStacks call. CDK's own BootstrapVersion parameter is referenced only by Rules, so it does not count.
  • No CloudFormation stack of that name. There is nothing to compare, and the import behaves as it would without this check.

Stacks imported with cdkd 0.290.35

cdkd 0.290.35 stored the "no baseline" decision without its cause, and cdkd 0.290.35 and 0.290.36 both remove a decision that has no cause. If a stack imported with 0.290.35 has since had one of these resources updated by either version, the deployed parameter value may be in state.json. Rotate the secret, then follow Import internals.

A secret set outside your template

cdkd can keep a secret out of the baseline only where your template spells a {{resolve:...}} reference for it. A secret with no reference in the template is recorded exactly as AWS returned it. A value your template never references lists the ways that happens and why cdkd does not prevent it.

Two details add to that summary:

  • Inside a property that does carry a reference, AWS can return a field or list element the template does not have. cdkd records it as returned. Where that leaves a reference in the property unmatched, the position may be masked as *** instead.
  • CloudFormation behaves the same way for a property the template sets. Its drift detection reports the actual value (ActualProperties in DescribeStackResourceDrifts), leaving out only a value the service never returns. cdkd's baseline also holds the properties the template never sets, and it is stored in state.json.

Remove a secret that reached state

  1. Rotate the secret through a reference

    Store the new value in Secrets Manager, write {{resolve:secretsmanager:...}} at that property in the template, and deploy. The deploy sends the new value to AWS, and the next baseline records the reference. Confirm with:

    cdkd scrub MyStack --dry-run --fail
    

    Do not rotate in the console. That sets another value outside the template, which cdkd records the next time it reads the resource.

  2. If the template has no property to put a reference on

    This is the case for a field AWS added, or for a property of a Cloud Control resource that the template never sets. No cdkd command removes such a value in place. Rotate the secret at its source first, then remove the value as described under Where the value sits.

    Check any stack output that publishes the value as well. An output is stored in the stack's outputs, and an exported output is also stored in the region's exports index, the file cdkd uses to resolve cross-stack references.

  3. Delete the earlier versions of the state file, last

    cdkd scrub MyStack --purge-history
    

    The state bucket is versioned, so earlier copies of state.json still hold the old value. This command deletes the earlier versions of every state.json it examines, including one it does not rewrite. It does not delete the exports index's earlier versions unless the same run rewrote that index. cdkd scrub describes what the purge does not reach.

Where the value sits

For a value the template has no property for, the removal depends on where it is in the record.

Where How to remove it
A nested field or list element inside a property Remove it from the AWS resource, then run cdkd state refresh-observed MyStack or cdkd drift MyStack --accept.
A top-level key Remove it from the AWS resource, or delete that key from the record's observedProperties in state.json by hand.
A Cloud Control resource's attributes Also delete the key from attributes by hand. Neither command rewrites attributes.

Two rules for editing state.json by hand:

  • Do not delete a nested field from observedProperties. cdkd drift compares nested fields against what AWS returns, so every later run would report the field and print the value.
  • For a top-level key, delete only that key. If you delete the whole observedProperties object, any cdkd deploy fills it again from AWS.

A hand edit lasts only while AWS no longer holds the value. cdkd records it again the next time it reads the resource: on a deploy that updates the resource, on cdkd state refresh-observed, or on cdkd drift --accept. An edit to attributes lasts until the next deploy that updates the resource, which writes every property back.

Last updated: