To retain, or not to retain resources in Cloudformation stacks
This is yet-another installment in series notes-to-self, inspired by theburningmonk.com and conversation in LinkedIn …
By default Cloudformation will delete a resource when the stack is deleted or resource is removed
from template. Resources can also be replaced, that is deleting existing resource after new is
created during stack update. When you have persistent data stored into database or disk, you
would want to retain rather then delete. Resource retention can be controlled with DeletionPolicy
and UpdateReplacePolicy
attributes.
What is DeletionPolicy?
DeletionPolicy is an optional attribute you can use to preserve a resource when it would otherwise be deleted due to stack deletion or update where resource is removed from the template. For EBS volumes, databases and Elastic caches, you can also take a final snapshot before resource deletion.
Use-cases for DeletionPolicy
The Main use-case for DeletionPolicy
attribute is to protect stateful resources being deleted
during stack update or deletion.
Secondary use-case is, if you are not planning to maintain resources via Cloudformation stacks,
setting DeletionPolicy
when creating stack and resources, with pre-defined standard architecture,
and then delete the stack but retain resources it created.
Retail or RetainExceptOnCreate?
DeletionPolicy
can have 4 different values, depending what you want to happen when resource
is about to be deleted.
Delete
is the default when you don’t specify ´DeletionPolicy` and resource will be deleted.Retain
will skip the deletion and resource will remain as-is, except it isn’t part of cloudformation stack any longer.Snapshot
will take final snapshot before deleting the resource. This option is available only for EBS volumes, Elastic caches and certain databases.RetainExceptOnCreate
is the latest addition that works the same way asRetain
but won’t retain resource if initial stack creation or resource creation at stack update ends with rollback.
So, should you use Retain
or RetainExceptOnCreate
? I really don’t see any reasons why Retain
would be better than RetainExceptOnCreate. Original Retain
is preserved for backwards compatibility
but RetainExceptOnCreate
does cleanup after a failed stack creation or update automatically.
Even when you are using stacks just to create resources, but not maintaining them through updates,
this retains resources because stack deletion is separete operation after successful creation.
UpdateReplacePolicy
If you read above carefully, you noted I said “deleted”. But resources can also be replaced, i.e.
creating a replacement and then deleting the original. During stack updates it can be difficult
to see beforehand what changes will trigger resource replacement. Creating a change set
will reveal this, so you can avoid doing it accidentially, but sometimes replacements just can not be
avoided and then UpdateReplacePolicy
is the way to retain your data.
UpdateReplacePolicy
has the same options as DeletionPolicy
, except you can not use RetainExceptOnCreate
as it doesn’t make sense for resource updates.
How to apply DeletionPolicy (and UpdateReplacePolicy)?
The Most simple way, when you need just an on/off -switch, is using If
with Condition
.
Ability to include functions in policies was introduced in AWS::LanguageExtensions
transform, but it is now part of standard Cloudformation and transform is no longer required.
# Oct12th 2023, AWS::LanguageExtensions is no longer needed for
# intrinsic functions in DeletionPolicy or UpdateReplacePolicy.
# Transform: AWS::LanguageExtensions
Parameters:
Environment:
Type: String
Default: dev
AllowedValues:
- dev
- test
- prod
Description: Environment type
Conditions:
IsProd: !Equals [ !Ref Environment, "prod" ]
Resources:
MyDB:
Type: AWS::RDS::DBInstance
DeletionPolicy: !If [ "IsProd", "RetainExceptOnCreate", "Delete" ]
UpdateReplacePolicy: !If [ "IsProd", "Retain", "Delete" ]
Properties:
...
If you need more than on/off -switch, e.g. there are more than one environment where you would want to retain resources, it might be easier to create a map than maintain complex conditions clauses.
# Oct12th 2023, AWS::LanguageExtensions is required for FindInMap default value feature.
Transform: AWS::LanguageExtensions
Parameters:
Environment:
Type: String
Default: dev
Description: Environment type, e.g. dev, test, stage, prod
Mappings:
Policies:
stage:
Delete: RetainExceptOnCreate
Update: Retain
prod:
Delete: RetainExceptOnCreate
Update: Retain
Resources:
MyDB:
Type: AWS::RDS::DBInstance
DeletionPolicy: !FindInMap [ Policies, Environment, Delete, DefaultValue: Delete ]
UpdateReplacePolicy: !FindInMap [ Policies, Environment, Update, DefaultValue: Delete ]
Properties:
...
Above would allow you to use freeform environment names and retain resources when environment is
stage
or prod
. In all other environments, resources will be deleted. Note that DefaultValue
feature in FindInMap
is also part of AWS::LanguageExtensions
.
How to apply DeletionPolicy for all resources in template?
Problem with above solution, is you need to add policy attribute(s) for every resource in template individually. It would be nice if there was a switch in Cloudformation API you could turn on and set Retain(ExceptOnCreate) policy for every resource. Unfortunately there isn’t such a thing, but you could get close to this by building a custom transformation.
I will leave the implementation of such transform for reader as homework, but if you do this, please drop me a note where can I find your implementation ;-)