DEV Community

Cover image for Import console created resources into CloudFormation

Import console created resources into CloudFormation

This one's for beginners.

Think about each time you created and configured some new AWS services from the console, got really happy about it when you got it right but in the end thought it's going to be a mess to do it all over again.

I'm here to tell you that Infrastructure as a Code will save your ass so don't fight it. It will hurt in the beginning as it's a tough learning curve but it pays off on any given Sunday.

I decided to write this blogpost after migrating 80+ resources across 11 services into CloudFormation because I know it hurts to start something like this and I wanted to give you the push that you needed.

I'm writing IaC on a regular basis in Terraform and CloudFormation, this time I used CloudFormation because I was curious how the import feature works, since I didn't use until now.

Let's talk about the benefits of importing your existing resources into IaC:

  • version control: track every infrastructure change in git
  • disaster recovery: recreate your entire infrastructure from code
  • consistency: deploy identical environments (dev/staging/prod)
  • change preview: see what will change before applying
  • team collaboration: code reviews for infrastructure changes
  • dependency management: CloudFormation prevents breaking changes

There are a few cons on the other hand:

  • console updates: you can still manually modify resources in the console
  • drift detection: you need to actively check for drift (manual changes done from the console, not via IaC)

The import process: Overview

  • create a CloudFormation template describing your existing resources
  • create an import changeset specifying which resources to import
  • execute the changeset to bring resources under CloudFormation management

Sounds simple but the devil is in the details.

Lessons learned

1. Never use create-stack for existing resources

aws cloudformation create-stack \
  --stack-name network \
  --template-body file://network.yaml
Enter fullscreen mode Exit fullscreen mode

CloudFormation creates NEW resources instead of importing existing ones. You'll end up with duplicate VPCs, subnets and a mess to clean up.

Always use import changesets:

aws cloudformation create-change-set \
  --stack-name network \
  --change-set-name import-network \
  --change-set-type IMPORT \
  --resources-to-import file://resources.json \
  --template-body file://network.yaml
Enter fullscreen mode Exit fullscreen mode

2. Not all resource types support import

CloudFormation import has limitations. Some resource types simply don't support it.

Resources that DON'T support import:

  • AWS::Route53::RecordSet - DNS records
  • route table associations
  • VPC gateway attachments

Workarounds (any will do):

  • manage these outside CloudFormation
  • delete and recreate them (accept brief downtime)
  • use Terraform instead (supports more imports)

Example: For Route53 records, I deleted existing records and let CloudFormation recreate them. The 5-10 second downtime was acceptable for full IaC coverage.

3. DeletionPolicy is mandatory for imports

Use the DeletionPolicy: Retain to every resource definition or you'll get the Resources must have DeletionPolicy attribute specified in the template error

CloudFormation needs to know what to do if you delete the stack. Retain means "keep the resource even if the stack is deleted."

4. Outputs can't be added during import

Use a two-step process to avoid the You cannot modify or add [Outputs] during import operations error:

  • Import resources without Outputs section
  • Update stack to add Outputs after import completes

5. Resource names must match exactly

Your template uses a friendly name but AWS has a different name. Use exact AWS names during import, rename later via stack update to aviod the The Identifier [AlarmName] does not match the identifier value in the template error.

6. Beware of circular dependencies

Security groups often reference each other:

  • SG-A allows traffic from SG-B
  • SG-B allows traffic from SG-A

If you use cross-stack references, you create a deadlock:

  • Stack A exports SG-A, imports SG-B
  • Stack B exports SG-B, imports SG-A
  • Neither can be created or updated!

Keep mutually referencing resources in the same stack or as a last resort, use hardcoded IDs for cross references.

7. Start with minimal outputs

Export only what's actually used to aviod dependencies that block stack updates and deletions.

8. Modular stacks are better than monoliths

The monolith approach can break the stack because one change requires updating entire stack, it's difficult to understand and maintain, you have long deployment times:

infrastructure.yaml (5000 lines)
├── VPC
├── Subnets
├── EC2 Instances
├── Security Groups
├── CloudFront
├── Route53
├── S3
└── Everything else
Enter fullscreen mode Exit fullscreen mode

The modular approach:

templates/
├── network.yaml (VPC, subnets)
├── compute.yaml (EC2, security groups)
├── cdn.yaml (CloudFront, WAF)
├── dns.yaml (Route53)
└── storage.yaml (S3)
Enter fullscreen mode Exit fullscreen mode

You update only what changed, have a smaller blast radius, it's easier to understand and you get faster deployments.

Import strategy

Inventory and planning

  • document existing resources: list everything you need to import
  • check import support: verify each resource type supports import
  • identify dependencies: map relationships between resources
  • plan stack structure: decide on modular vs monolithic approach

Create import templates

  • start simple: begin with standalone resources (S3, SNS)
  • add DeletionPolicy: every resource needs DeletionPolicy: Retain
  • use exact names: match AWS resource names exactly
  • skip Outputs: don't add Outputs section yet
  • test in dev: always test import process in non-production first

Execute imports

  • create import changeset: use --change-set-type IMPORT
  • review changes: check what will be imported
  • execute changeset: bring resources under IaC management
  • verify: confirm resources are managed by CloudFormation

Improve templates

  • add Outputs: update stacks to add cross-stack exports
  • replace hardcoded values: Use !ImportValue for dynamic references
  • document: add comments explaining configurations

Conclusion

  • always use import changesets, never create-stack
  • not all resources support import so plan accordingly
  • DeletionPolicy is mandatory for imports
  • start modular, multiple focused stacks beat monoliths
  • minimal exports, avoid dependency hell
  • test in dev first: always
  • accept brief downtime for resources that don't support import
  • document everything, your future self will thank you

Resources

AWS CloudFormation import documentation
Resource types that support import
CloudFormation best practices

Top comments (0)