cdkd export: Custom Resources and IAM policies
CloudFormation cannot import two kinds of resource that CDK stacks often
contain: Custom Resources and AWS::IAM::Policy. cdkd export
migrates them in a second phase. Phase 1 imports everything else. Phase 2 then
submits the full template as an UPDATE changeset, and CloudFormation creates
these resources itself.
The two kinds differ in what you have to do:
| Resource | Needs a flag | What phase 2 costs |
|---|---|---|
| Custom Resource | --include-non-importable |
The backing Lambda is invoked with a Create event again. |
AWS::IAM::Policy |
no | The permission is missing until phase 2 completes. |
cdkd export MyStack --include-non-importable --dry-run # preview both phases
cdkd export MyStack --include-non-importable
Custom Resources and --include-non-importable
A Custom Resource is backed by a Lambda function that you or a CDK construct
wrote. In the template its type is Custom::* or
AWS::CloudFormation::CustomResource. The second is what
new cdk.CustomResource(...) synthesizes when no resourceType is passed.
Without --include-non-importable, cdkd refuses a stack that contains one.
Under --dry-run the refusal becomes a warning, so you still see the full
plan.
With the flag, phase 2 has CloudFormation create the Custom Resources. To do
so, CloudFormation invokes each backing Lambda's onCreate handler again,
although the resource already exists.
What the handler must do
The handler has to cope with that second Create event:
- It must be idempotent: it returns the same
PhysicalResourceIdandDataon every event type. - It must answer through the response URL: it sends its status payload with
an HTTP
PUTtoevent.ResponseURL.
Warning
cdkd's own deploy also accepts a handler that only returns a value. CloudFormation does not. A Custom Resource backed by such a Lambda hangs phase 2, and every later
cdk deploy, until CloudFormation's one-hour Custom Resource timeout.
AWS::IAM::Policy is deleted and re-created
An AWS::IAM::Policy is an inline policy attached to roles, users or groups.
CDK grants emit this type often: a Lambda execution role's inline policy, or
an ECS task execution role's ECR pull policy. CloudFormation has no way to
look up an existing inline policy, so it cannot import one.
cdkd handles this type without a flag:
- Phase 1 leaves the policy out.
- cdkd deletes the inline policy from its roles, users and groups.
- Phase 2 has CloudFormation create it again from the template.
Warning
Between steps 2 and 3 the permission does not exist. A call that relies on it fails with
AccessDenieduntil phase 2 completes.
The plan that cdkd prints before the confirmation lists each such policy, and
the roles, users and groups it will be removed from. To refuse such a stack
instead, pass --no-recreate-import-unsupported.
The policy is checked against the template first
Step 2 and step 3 take their principals from different places. cdkd detaches the policy from the principals recorded in cdkd state. CloudFormation attaches it to the principals named in the template. If the two lists differ, a principal could be detached and not attached again.
cdkd therefore compares the state record with the template before anything changes. When they disagree, it stops the export with no AWS change, and the error tells you what to run:
- State records a principal the template does not name, or a policy name
other than the template's
PolicyName. This is usually a template change that has not been deployed. Runcdkd diff, thencdkd deploy. If the value comes from a root Parameter, run the export again with the--parametervalues the stack was deployed with. - State records no principal list, a value that is not a list of IAM names,
or a physical id that is not the policy name. Import the policy again
with
cdkd import ... --force, so that its record matches the template. - State records more than 100 principals. Check the record and the template against AWS. Do not destroy the resource or remove it from the app: that removes the policy from every recorded principal.
Principals cdkd cannot check
cdkd can check a template principal that is one of these:
- a literal name,
- a
Refto a role, user or group in the same template, - a
Refto a template Parameter.
It cannot check any other value, such as Fn::ImportValue, and it cannot
check a missing PolicyName. The plan marks each one it could not check, and
the export proceeds.
Important
Under
--yesnobody reads those marks. A principal that the template names only through an uncheckable value is detached and may not be re-attached. Read a--dry-runplan before exporting such a stack unattended.
If deleting the policy fails
The export stops between phase 1 and phase 2. cdkd state and the CloudFormation stack that phase 1 created are both kept.
The usual cause is a missing permission: iam:DeleteRolePolicy,
iam:DeleteUserPolicy or iam:DeleteGroupPolicy, depending on what the
policy is attached to.
Running cdkd export again does not resume the export, because the
CloudFormation stack now exists. The error gives the steps to finish by hand:
- Delete the remaining inline policies.
- Run the phase-2 UPDATE yourself.
- Remove the state record with
cdkd state orphan.
If phase 2 fails
cdkd keeps its state record, and the CloudFormation stack from phase 1 stays. The error prints the recovery:
- An
aws cloudformation create-change-set --change-set-type UPDATEcommand that finishes phase 2. cdkd state orphan, which removes the state record once the update has succeeded.
Related
cdkd export: the command, the run order and the options- cdkd export: what blocks an export: types that cannot be migrated at all
- cdkd export: nested stacks: phase 2 inside a nested-stack tree
- cdkd export internals: the pre-delete check in detail, for contributors