Skip to content
cdkd

Provisioning Layers

cdkd creates, updates and deletes every resource in one of two ways, called provisioning layers:

  • An SDK provider is code inside cdkd, written for one resource type, that calls that service's API directly. It is fast, but it handles only the properties it was written for.
  • The Cloud Control API is AWS's generic API for managing resources. It handles every property in the type's CloudFormation schema, but each call is slower.

cdkd uses the SDK provider where it can and Cloud Control for everything else. Supported Resources lists which types have an SDK provider.

cdkd chooses the layer for you, so most of the time you can ignore it. This page is for the three situations where the layer shows:

  • a deploy is slower than you expected,
  • cdkd diff marks a resource with [via CC API: <property>],
  • a property you set does not appear in AWS.

How cdkd picks

On every deploy, cdkd asks three questions about each resource:

How cdkd picks the layer for a resourcecdkd asks three questions about a resource on every deploy. Is it already recorded as cc-api in state? If yes, it stays on the Cloud Control API, apart from a few types that move back. If no: does an SDK provider exist for its type? If none exists, the Cloud Control API handles it. If one exists: does the provider handle every top-level property in the template? If it would leave a property out, the resource goes through the Cloud Control API. Otherwise the SDK provider handles it.noyesyesAlready recorded as cc-api in state?Yes: it stays on the Cloud Control API,apart from a few types that move back.Does an SDK providerexist for the type?No: the Cloud Control API handles it.Does it handle every top-levelproperty in the template?No: it goes through the Cloud ControlAPI, so the property reaches AWS.SDK providerThe fast path.How cdkd picks the layer for a resourcecdkd asks three questions about a resource on every deploy. Is it already recorded as cc-api in state? If yes, it stays on the Cloud Control API, apart from a few types that move back. If no: does an SDK provider exist for its type? If none exists, the Cloud Control API handles it. If one exists: does the provider handle every top-level property in the template? If it would leave a property out, the resource goes through the Cloud Control API. Otherwise the SDK provider handles it.noyesyesAlready recorded as cc-api in state?Yes: it stays on the Cloud Control API,apart from a few types that move back.Does an SDK provider exist for the type?No: the Cloud Control API handles it.Does it handle every top-levelproperty in the template?No: it goes through the Cloud ControlAPI, so the property reaches AWS.SDK providerThe fast path.
  1. Is the resource already on Cloud Control? cdkd records the layer of every resource in state, its record of what it deployed. A resource recorded as cc-api stays on Cloud Control, with the exceptions described under Moving back to the SDK provider.
  2. Does cdkd have an SDK provider for the type? If not, the resource goes through Cloud Control.
  3. Does the SDK provider handle every top-level property the template sets? If the provider would leave one out, cdkd sends the resource through Cloud Control, so that the property reaches AWS.

When all three answers allow it, the SDK provider handles the resource.

A resource that the deploy replaces counts as a new resource. Question 1 does not apply to it, and cdkd chooses its layer from questions 2 and 3.

Example: one new property changes the layer

Suppose HttpApi is deployed through its SDK provider, and you then set a property that the provider does not handle. cdkd diff shows what the next deploy will do:

  [~] HttpApi (AWS::ApiGatewayV2::Api) [via CC API: Body]

The annotation says that the resource will go through Cloud Control because of the Body property. You do not have to do anything. The next cdkd deploy updates the resource through Cloud Control, and the property reaches AWS.

For most types this is an update in place: the resource keeps its physical ID and nothing is recreated. Whether that works depends on the type, because the ID that the SDK provider stored must also be an identifier Cloud Control accepts.

See which layer a resource is on

cdkd state show MyStack
MyTopic
  Type: AWS::SNS::Topic
  PhysicalID: arn:aws:sns:us-east-1:123456789012:MyTopic
  ProvisionedBy: cc-api

ProvisionedBy is sdk or cc-api. A third value, (sdk, legacy default), appears on a resource deployed by a cdkd version that did not record the layer yet. cdkd treats such a resource as managed by the SDK provider, and chooses its layer afresh on the next deploy.

Keep a resource on the SDK provider: --prefer-sdk-route

If you would rather keep the faster SDK provider and do without the property, name the type and the property:

cdkd deploy MyStack --prefer-sdk-route AWS::ApiGatewayV2::Api:Body

The resource stays on the SDK provider, and the property is not sent to AWS. The flag applies to the deploy you pass it to. On a later deploy without the flag, cdkd sends the resource through Cloud Control and the property reaches AWS.

Moving back to the SDK provider

A resource on Cloud Control stays there. This is deliberate: when a new cdkd release adds an SDK provider for a type, your existing resources of that type do not change layer underneath you.

Some types are exceptions and do move back on their own. For these types the SDK provider finds the resource by the same physical ID that Cloud Control stored, so the move is an update in place. Nothing is destroyed, references to the resource keep working, and configuration attached to the resource outside cdkd survives.

There are two kinds of exception:

  • Cloud Control cannot manage the type correctly. The resource moves to the SDK provider unconditionally, and you cannot decline.
  • Cloud Control works but is slower, and cdkd now handles every property of the type. The resource moves on the next deploy that changes it, provided that neither the template nor state holds a property the SDK provider would leave out. You can decline for one deploy with --pin-cc-api.

cdkd diff shows the move before it happens:

  [~] MyTopic (AWS::SNS::Topic) [returning to SDK provider]

cdkd deploy names it as it happens:

MyTopic (AWS::SNS::Topic): returning to the SDK provider — cdkd now covers every property this resource uses. The physical id is preserved; pass --pin-cc-api MyTopic to decline this for a deploy.

The set of types that move back grows with cdkd's coverage, so this page does not list them. A resource of any other type can still move back, but only by being destroyed and recreated, as the next section describes.

Move a resource by recreating it

Two flags move one resource to the other layer by destroying it and creating it again:

# SDK provider -> Cloud Control
cdkd deploy MyStack --recreate-via-cc-api MyFunction

# Cloud Control -> SDK provider
cdkd deploy MyStack --recreate-via-sdk-provider MyFunction

Use --recreate-via-cc-api when a property needs Cloud Control and the update in place cannot deliver it. Use --recreate-via-sdk-provider to bring a resource back when its type does not move back on its own.

Warning

Both flags destroy the resource and create a new one. Each names one resource and asks for confirmation first. Both refuse a stateful type unless you also pass --force-stateful-recreation, which accepts losing its data.

Which flag to use

You want Do this
A property the SDK provider leaves out to reach AWS Nothing; cdkd uses Cloud Control
The same, when the update in place cannot deliver it --recreate-via-cc-api <LogicalId>
To stay on the SDK provider without the property --prefer-sdk-route <Type>:<Prop>
To move a resource back to the SDK provider Nothing, if its type moves back on its own
The same, for any other type --recreate-via-sdk-provider <LogicalId>
To decline an automatic move back, for one deploy --pin-cc-api <LogicalId>

--pin-cc-api destroys nothing. It declines the move for one deploy and is not stored, so it does not set a lasting preference. It has no effect on a type that moves back because Cloud Control cannot manage it.

cdkd deploy: safety & compatibility flags documents each flag in full, with its guards and refusals.

Edge cases

A create-only property that an earlier deploy left out

A create-only property can be set only when a resource is created. Suppose an earlier deploy left one out because you passed --prefer-sdk-route. cdkd remembers that. If a later deploy would send the property, cdkd cannot apply it to the live resource, so the deploy is refused with CREATE_ONLY_DROP_NEEDS_REPLACEMENT.

A later deploy would send the property in two cases: you removed the flag, or another property that the SDK provider leaves out moved the whole resource to Cloud Control.

To proceed, opt into replacing the resource with --recreate-via-cc-api or --replace.

The refusal comes when the deploy reaches that resource. Other resources in the same deploy may already have changed by then, and they are rolled back together with it.

The refusal applies only to a property that an earlier deploy recorded as left out. For an imported resource, and for a resource whose state was written by an older cdkd, cdkd assumes the create-only properties are already in AWS.

Names of resources the template does not name

A resource your template does not name gets the same generated name on either layer, so moving it between layers or recreating it on the other one never changes the name cdkd expects. cdkd never sends a CodeBuild project or a budget through Cloud Control, so their SDK providers always name them.

An AWS::Lambda::Permission has no name property; its statement id is the name. The SDK provider generates it as <StackName>-<LogicalId>-<suffix>, as CloudFormation does, with a new one each time the permission is replaced, and Cloud Control's handler picks its own. A stack without stack instance ids keeps sending the bare logical id.

A stack first deployed by a cdkd without stack instance ids keeps the names each layer gave before:

Type SDK provider Cloud Control
AWS::Logs::LogGroup /cdkd/<StackName>-<LogicalId> <StackName>-<LogicalId>
AWS::SSM::Parameter /<StackName>-<LogicalId> <StackName>-<LogicalId>
AWS::CodeBuild::Project <LogicalId> No Cloud Control route
Auto Scaling group, DynamoDB global table, ECS task definition family, Lambda network connector, EventBridge pipe, EventBridge Scheduler schedule <StackName>-<LogicalId> Named by AWS
EC2 security group, CloudTrail trail, Glue job, crawler, connection and trigger, Firehose delivery stream <LogicalId> Named by AWS

Neither layer generates the name of an AWS::Events::EventBus, which CloudFormation requires. A Cloud Control create of an un-named AWS::S3Express::DirectoryBucket reads the zone id the name must end with from LocationName. When LocationName is an Availability Zone name rather than a zone id such as use1-az5, the create is refused; set LocationName to the zone id or declare BucketName.

A generated security group name never starts with sg-, which EC2 refuses: for a stack whose name starts with sg- in any case, the hyphen is dropped.

Last updated: