CloudFormation 2 – importing resources into a CDK stack

In my previous post I mentioned that I was starting to migrate some existing CloudFormation templates to be generated using TypeScript and Amazon’s Cloud Development Kit. I started with some simple static websites that are hosted from S3 buckets, and for these simple sites found it easiest to completely tear down the old infrastructure and recreate it with the CDK. I’m now starting to work on infrastructure that I don’t want to destroy – systems like the EC2 instance that host this blog.

That post also mentioned the possibility to import existing resources into a CloudFormation stack. The CDK doesn’t provide support for this, but it’s still possible. Let’s start with a simple resource that I can recreate if something goes wrong: an IAM user.

Removing the user from the old stack

Let’s start with a simple stack (OldStack) that contains a user (Leigh), and investigate how we can move this to a new stack (NewStack) managed using the CDK. Here’s the old stack:

Resources:
  Leigh:
    Type: AWS::IAM::User
    Properties:
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AdministratorAccess
        - arn:aws:iam::aws:policy/job-function/Billing
      UserName: leigh

We want to drop the user from this stack so that we can use it in the new one. We can do this by adding a DeletionPolicy and then removing it (in two separate steps). First add the policy:

 Resources:
   Leigh:
     Type: AWS::IAM::User
+    DeletionPolicy: Retain
     Properties:
       ManagedPolicyArns:
         - arn:aws:iam::aws:policy/AdministratorAccess
         - arn:aws:iam::aws:policy/job-function/Billing
       UserName: leigh

Deploy the new template so the policy is applied within CloudFormation. We can then remove the resource from the stack and deploy again. For this example we only have one resource, so let’s just delete the whole stack. Here’s the stack events as reported by the AWS console:

The user wasn’t deleted because we added the DeletionPolicy. We now have an orphaned IAM user that’s not attached to any stacks.

Building the new stack

Let’s build a new stack within the CDK that will (eventually) host it. Here’s some code for an empty stack:

import { Stack } from '@aws-cdk/core';
import type { Construct, StackProps } from '@aws-cdk/core';

class NewStack extends Stack {
    constructor(scope: Construct, id: string, props?: StackProps) {
        super(scope, id, props);
    }
}

export default NewStack;

We can deploy this as usual with the CDK.

Preparing the template for import

Once the new stack has been created we’re ready to import the user. First we need to add code that describes it:

+import { User } from '@aws-cdk/aws-iam';
 import { Stack } from '@aws-cdk/core';
 import type { Construct, StackProps } from '@aws-cdk/core';

 class NewStack extends Stack {
     constructor(scope: Construct, id: string, props?: StackProps) {
         super(scope, id, props);
+
+        new User(this, 'User', {
+            userName: 'leigh',
+            managedPolicies: [
+                { managedPolicyArn: 'arn:aws:iam::aws:policy/AdministratorAccess' },
+                { managedPolicyArn: 'arn:aws:iam::aws:policy/job-function/Billing' },
+            ],
+        });
     }
 }

 export default NewStack;

We can now generate the stack template using cdk synth:

$ cdk synth NewStack
Resources:
  User00B015A1:
    Type: AWS::IAM::User
    Properties:
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AdministratorAccess
        - arn:aws:iam::aws:policy/job-function/Billing
      UserName: leigh
    Metadata:
      aws:cdk:path: NewStack/User/Resource
  CDKMetadata:
    Type: AWS::CDK::Metadata
    Properties:
      Analytics: v2:deflate64:H4sIAAAAAAAAAyWMQQqAIBAA39LdtoQIugX9oOgBsm1gkcKu1kH8e0mnOcwwGoYOdDWaR2rcziahZ4K0BIOnmryTwBGDmnY3k/jISFmV1poL0irERRXmrJzfCA5pbv0te2irQ6ytObpgL4L55wuPOD5FcQAAAA==
    Metadata:
      aws:cdk:path: NewStack/CDKMetadata/Default

Unfortunately this template can’t be used to import the user:

  • “Each resource to import must have a DeletionPolicy attribute in your template” (source) but this is missing.
  • The AWS::CDK::Metadata resource changed when generating the updated template. CloudFormation doesn’t allow you to change existing resources at the same time as an import.

Luckily these problems can easily be resolved manually. We can add a DeletionPolicy and revert the metadata to match the value that’s currently deployed (you can get this from the Template tab within CloudFormation). Save the YAML output from cdk synth or edit the template directly (by default this is written to a cdk.out directory).

 Resources:
   User00B015A1:
     Type: AWS::IAM::User
+    DeletionPolicy: Retain
     Properties:
       ManagedPolicyArns:
         - arn:aws:iam::aws:policy/AdministratorAccess
         - arn:aws:iam::aws:policy/job-function/Billing
       UserName: leigh
     Metadata:
       aws:cdk:path: NewStack/User/Resource
   CDKMetadata:
     Type: AWS::CDK::Metadata
     Properties:
-      Analytics: v2:deflate64:H4sIAAAAAAAAAyWMQQqAIBAA39LdtoQIugX9oOgBsm1gkcKu1kH8e0mnOcwwGoYOdDWaR2rcziahZ4K0BIOnmryTwBGDmnY3k/jISFmV1poL0irERRXmrJzfCA5pbv0te2irQ6ytObpgL4L55wuPOD5FcQAAAA==
+      Analytics: v2:deflate64:H4sIAAAAAAAAAzPUszTRM1R0SCwv1k1OydZPzi9K1asOLklMztZxzs8rLikqTS7RcU7LC0otzi8tSk6t1cnLT0nVyyrWLzME6jTTM1DMKs7M1C0qzSvJzE3VC4LQAMvlLV1YAAAA
     Metadata:
       aws:cdk:path: NewStack/CDKMetadata/Default

Is it safe to deploy with out-of-date metadata? I think so. If we decode the metadata values (these are base64-encoded gzip) then we get the following:

$ echo -n 'H4sIAAAAAAAAAzPUszTRM1R0SCwv1k1OydZPzi9K1asOLklMztZxzs8rLikqTS7RcU7LC0otzi8tSk6t1cnLT0nVyyrWLzME6jTTM1DMKs7M1C0qzSvJzE3VC4LQAMvlLV1YAAAA' | base64 -d | gunzip
1.94.1!@aws-cdk/core.{Stack,Construct,CfnResource},node.js/v14.16.0!jsii-runtime.Runtime
$ echo -n 'H4sIAAAAAAAAAyWMQQqAIBAA39LdtoQIugX9oOgBsm1gkcKu1kH8e0mnOcwwGoYOdDWaR2rcziahZ4K0BIOnmryTwBGDmnY3k/jISFmV1poL0irERRXmrJzfCA5pbv0te2irQ6ytObpgL4L55wuPOD5FcQAAAA==' | base64 -d | gunzip
1.94.1!@aws-cdk/{core.{Stack,Construct,CfnResource},aws-iam.{User,CfnUser}},node.js/v14.16.0!jsii-runtime.Runtime

This doesn’t look very important, especially when the data is stored under a key called “Analytics”. I can’t find any documentation for the AWS::CDK::Metadata resource, so think it’s reasonable to conclude this doesn’t have any meaningful impact on the environment. In any case we’ll fix it later.

Importing the user into the new stack

Select the new stack and choose Import resources into stack to get the process started:

Upload the template that we’ve edited:

Tell CloudFormation how to find the user we want to import:

Preview the changes and import:

Cleaning up

We’ve now successfully imported the user into the new stack, but the template doesn’t match that defined using the CDK because of the manual changes we made:

$ cdk diff NewStack
Stack Root
Resources
[~] AWS::IAM::User User User00B015A1
 └─ [-] DeletionPolicy
     └─ Retain

Deploy the stack in order to revert these changes and bring CloudFormation into alignment with the CDK code.

This entry was posted in Uncategorized. Bookmark the permalink. Trackbacks are closed, but you can post a comment.

Post a Comment

Your email is never published nor shared. Required fields are marked *

You may use these HTML tags and attributes <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*
*