CloudFormation will happily keep around old, unused versions of resource types forever, and they can silently consume resources and cost.
Here’s how to identify and clean them up.
First, let’s see what’s actually installed and in use. You can list all registered resource types and their versions with the AWS CLI:
aws cloudformation list-type-versions --type RESOURCE
This will give you output like:
{
"SummarizeResourceTypes": [
{
"Arn": "arn:aws:cloudformation:us-east-1::type/resource/AWS-EC2-Instance",
"TypeName": "AWS::EC2::Instance",
"DefaultVersionId": "1",
"DeprecatedVersionId": null,
"LatestVersionId": "1",
"Type": "RESOURCE",
"Description": "AWS::EC2::Instance",
"Schema": "...",
"TimeCreated": "2016-10-05T17:29:30.000Z",
"TimeModified": "2016-10-05T17:29:30.000Z"
},
{
"Arn": "arn:aws:cloudformation:us-east-1::type/resource/AWS-S3-Bucket",
"TypeName": "AWS::S3::Bucket",
"DefaultVersionId": "2",
"DeprecatedVersionId": "1",
"LatestVersionId": "2",
"Type": "RESOURCE",
"Description": "AWS::S3::Bucket",
"Schema": "...",
"TimeCreated": "2018-01-15T10:00:00.000Z",
"TimeModified": "2020-03-20T14:30:00.000Z"
}
]
}
The key fields here are TypeName, LatestVersionId, DefaultVersionId, and DeprecatedVersionId.
LatestVersionId: The most recently published version of this resource type.DefaultVersionId: The version currently being used by your CloudFormation stacks. If a stack doesn’t explicitly specify a version, it uses the default.DeprecatedVersionId: A version that has been superseded by a newer one but is still available.
The problem arises when you have versions that are neither the default nor the latest, and crucially, are not actively used by any of your stacks. CloudFormation doesn’t automatically clean these up.
To find unused older versions, you need to compare the LatestVersionId and DefaultVersionId against all versions that have been registered for a given TypeName.
You can list all registered versions for a specific type using:
aws cloudformation list-type-versions --type RESOURCE --type-name AWS::S3::Bucket
This will show you all versions, including deprecated ones:
{
"TypeVersionSummaries": [
{
"Arn": "arn:aws:cloudformation:us-east-1::type/resource/AWS-S3-Bucket",
"VersionId": "1",
"IsDefaultVersion": false,
"TimeCreated": "2018-01-15T10:00:00.000Z"
},
{
"Arn": "arn:aws:cloudformation:us-east-1::type/resource/AWS-S3-Bucket",
"VersionId": "2",
"IsDefaultVersion": true,
"TimeCreated": "2020-03-20T14:30:00.000Z"
}
]
}
Now, for each TypeName from list-type-versions --type RESOURCE, you’d fetch all its versions and identify any VersionId that is not LatestVersionId and not DefaultVersionId, and also not DeprecatedVersionId (though deprecated versions are often good candidates for cleanup too, if you’re confident they aren’t implicitly used).
The critical step is to ensure no stack is currently referencing a specific older version. You can check this by inspecting the Resources section of your deployed stacks. For each stack, iterate through its resources and look at the ResourceType property. If it specifies a version (e.g., AWS::S3::Bucket::1), that version is in use.
A more programmatic way is to use the describe-stacks API for each of your stacks and parse the Resources field. You’d build a set of all ResourceType strings that include a version suffix (like ::1) and compare it against the list of all registered versions for each type.
Once you’ve identified a version that is not the default, not the latest, and not explicitly referenced by any stack, you can deregister it. Use the deregister-type command, specifying the Type and TypeName, and the VersionId you want to remove.
For example, to deregister version 1 of AWS::S3::Bucket:
aws cloudformation deregister-type --type RESOURCE --type-name AWS::S3::Bucket --version-id 1
This command will fail if the version is still the default or if CloudFormation detects any active usage. This is a safety mechanism. If it succeeds, the version is removed.
The most common reason this command fails is that the VersionId you’re trying to deregister is still set as the DefaultVersionId for that TypeName. To fix this, you must first set a new default version. You do this by specifying the DefaultVersionId when you register a new version, or by updating the default if a newer version is already registered.
If you want to make version 2 the default for AWS::S3::Bucket (assuming version 2 is already registered and is the latest):
aws cloudformation set-type-default-version --type RESOURCE --type-name AWS::S3::Bucket --version-id 2
After setting a new default, you can then proceed to deregister the old default version if it’s no longer needed.
Another common pitfall is trying to deregister a version that is marked as DeprecatedVersionId. While you can deregister deprecated versions, CloudFormation often marks them as deprecated rather than immediately making them deregisterable. You might need to explicitly set a newer version as default, and then attempt deregistration again.
The actual cost comes from custom resource types that you’ve developed or third-party types that are no longer maintained. These can still be billed by the underlying services they provision if not properly managed.
The next error you’ll encounter is TooManyResourceTypesFound if you’ve deregistered so many that you’re hitting account-level limits.