Skip to content
cdkd

Drift: what was not compared

A ! line in a cdkd drift report means cdkd could not compare a resource, or could compare only part of it. The resource may have drifted and cdkd cannot tell. This page explains each reason and how to clear it. The last section lists differences drift leaves out of the report on purpose.

The block looks like this:

  2 resource(s) NOT fully compared — 1 not compared AT ALL (the read or comparison failed), 1 only PARTIALLY compared (a dynamic reference cdkd could not, or refused to, resolve):
    ! ApiFunction (AWS::Lambda::Function) — the read or comparison threw, so NONE of its properties were compared
    ! Database (AWS::RDS::DBInstance) — cdkd refused to resolve a dynamic reference its state records (spell the reference as a full ARN, which names its region)

The heading counts two groups. "Not compared at all" means none of the resource's properties were checked. "Only partially compared" means one or more properties were left out and every other property was checked. Each ! line then names one resource and its reason.

With --json, the same resources are in the notCompared array, and each entry has a cause field holding one of the names below.

Why a resource was not compared

cause How much was compared Exit 2
readFailed Nothing Yes
readAborted Nothing Yes
baselineRefused Nothing Yes
unreadableRecord Nothing Yes
unreadableMap Nothing Yes
refused Part Yes
uncertifiedBaseline Part Yes
unresolvedToken Part No
noEchoParameter Part No

The last column applies to a run without --accept or --revert, and only when nothing drifted. A run that found drift exits 1 whatever else it found.

The two causes marked "No" are permanent. Running drift again can never compare the value, so if they produced exit 2, a CI job that checks drift would fail forever.

A resource that drifted can be partly compared too. Its reported changes are real. It is listed under both drifted and notCompared in --json, and its drifted entry carries referencesUnresolved: true.

A resource type cdkd cannot read back at all is a different outcome, drift unknown, and is marked ?. It is not a failed read.

readFailed: the read failed

cdkd tried to read the resource from AWS, or to compare it, and the attempt failed. The usual cause is a missing IAM permission or a throttled request.

    ! ApiFunction (AWS::Lambda::Function) — the read or comparison threw, so NONE of its properties were compared

Grant the missing permission, or run the command again if AWS was throttling.

readAborted: cdkd stopped reading the stack

Five reads in a row failed in the same stack, so cdkd stopped trying and marked the remaining resources as not read. Five consecutive failures usually mean the credentials expired, or the role lacks a permission every read needs, such as cloudcontrol:GetResource.

Refresh the credentials or restore the permission, then run the command again.

Two details limit how far this reaches. cdkd counts reads through the AWS Cloud Control API separately from reads through its own per-type code, so one route can stop while the other keeps working. A successful read resets the count. Other stacks in the same run are still read.

refused: a secret reference with no region

State holds a reference to a secret, and cdkd could not tell which region the secret lives in. Resolving it in the wrong region would compare against a different secret of the same name, so cdkd declines. The resource's secret properties are not compared, and its other properties are.

    ! Database (AWS::RDS::DBInstance) — cdkd refused to resolve a dynamic reference its state records (spell the reference as a full ARN, which names its region)

Write the reference in your CDK code as a full ARN, which includes the region.

This cause also appears on a nested stack when the state record of one of its parent stacks is missing or unreadable. A value the parent read from another region reaches the nested stack without its region, and cdkd needs the parent's record to work the region out.

uncertifiedBaseline: a masked secret position

State holds the mask *** at a position where the template has a secret reference, so that one position cannot be compared. Every other property of the resource is.

Run cdkd deploy for the stack. The cause and the fix are explained under A masked position cdkd could not certify.

baselineRefused: an import recorded no snapshot

The resource was adopted with cdkd import, and the import declined to record the snapshot drift compares against. Nothing about the resource is compared.

The fix depends on why the import declined; see Clearing a baseline refusal.

unreadableRecord and unreadableMap: damaged state

The state file itself is damaged. With unreadableRecord, one resource's entry is not readable as a resource, or its properties field is not an object. With unreadableMap, the whole resources map of the stack is not an object, and the report shows a single entry named (resources map).

Repair the state record, or import the stack again. To see the record as stored:

cdkd state show MyStack --json

unresolvedToken and noEchoParameter: permanent causes

These two do not change the exit code, because nothing you run can clear them.

In both cases every other property of the resource is compared.

When a secret reference cannot be resolved

A secret reference that cdkd tries to resolve and cannot is reported as a warning, and the resource's secret properties are not compared. The usual reasons are:

  • the role lacks secretsmanager:GetSecretValue or ssm:GetParameter;
  • the secret was deleted;
  • the reference points at another region.

Clearing a baseline refusal

A baseline refusal is the baselineRefused cause above: a cdkd import declined to record the snapshot of a resource. Until the refusal is cleared, drift does not compare the resource, and --accept and --revert both decline it. The resource has no trustworthy snapshot to accept into or revert to, and comparing it could print a decrypted secret.

cdkd tells you the fix for the specific resource in three places: the ! line in the report, the --accept / --revert refusal, and cdkd state show. The fix depends on why the import declined.

The import could not place the secret redaction

The resource's recorded properties did not let cdkd work out where the secret values sit. Deploy a change to that resource. A deploy that changes nothing does not clear the refusal.

The resource reads a template parameter cdkd could not prove

The resource uses a template parameter, and cdkd could not prove which value the parameter had when the resource was deployed. Two things clear it:

  • replacing the resource;
  • importing it again while a CloudFormation stack exists that can prove the value.

An update in place keeps the refusal.

The record has no reason

An older cdkd recorded the refusal without saying why. Treat it like the first case, unless the resource reads a template parameter. Then treat it like the second.

The full rules are in the drift baseline an import records.

Stacks imported before refusals were recorded

A cdkd release older than state schema v10 did not record refusals at all. A stack it imported is still compared, because nothing marks its resources.

cdkd drift warns about such a stack, naming the stack and the resources that have no snapshot. The warning stops after any later write to the state file, but the records are not fixed by that write. To fix them, run cdkd import for the stack again.

What is not reported as drift

Some differences between state and AWS are expected, and drift leaves them out of the report.

Difference Why drift ignores it
A key your template did not declare, recorded empty AWS or another resource fills it in later.
A tag whose key starts with aws: CDK and AWS add these.
An ELBv2 attribute AWS stopped returning AWS changes that set on its own.
A name with the stack-name prefix It is the same name.

Keys recorded empty

When cdkd recorded a key as empty ([], {} or null) and your template never declared it, a later value there is not drift. Another resource or AWS fills such keys in after the deploy. Examples are a cluster's CapacityProviders and the rules of a security group that are declared as separate resources.

A key your template did not declare is still compared when cdkd recorded a real value for it, such as a default AWS applied.

Tags

Tags with an aws: prefix, such as aws:cdk:path, are added by CDK and AWS and are not part of your Tags. Your own tags are compared for every type drift can read, in a stable order, so reordering tags is not drift. Inline policies on an IAM Role, User or Group are compared too.

Names prefixed with the stack name

On a stack deployed with --prefix-user-supplied-names, cdkd puts the stack name in front of the names you supply. State holds my-role and AWS holds MyStack-my-role, and drift treats them as the same name.

The rule covers an IAM Role, User, Group, InstanceProfile or ManagedPolicy, and an ELBv2 LoadBalancer or TargetGroup. A live name that cdkd would not derive from the template's name is still reported.

ELBv2 attributes AWS stops or starts reporting

LoadBalancerAttributes, TargetGroupAttributes and ListenerAttributes are recorded in full, including keys your template never declared. AWS sometimes stops or starts returning a key, such as ddos_protection.syn_cookie.mode on an ALB.

Case cdkd drift --revert
An undeclared key in state, absent from AWS Not reported Does not write it back
A key AWS returns that state lacks Reported Leaves the live value, and warns
A declared key that changes or disappears Reported Writes the recorded value
A changed value on a key both sides hold Reported Writes the recorded value

The second row is reported because cdkd cannot tell a key AWS started returning from a value someone set. If the live value is what you expect, run cdkd drift --accept to record it. Otherwise declare the value in your template and deploy.

Last updated: