[[AWS CloudFormation]] is great for performing atomic updates to a set of cloud resources, but sometimes it can get into a bad state or cause unwanted changes or even data loss.
Specifically, a CloudFormation stack update could:
1. Delete a resource which was accidentally removed by a developer from the CloudFormation template source
2. Delete a resource if a stack was accidentally deleted
3. Delete a resource whose attribute was changed in the CloudFormation template source. This would happen if the attribute that was updated was marked as "Update Requires: Replacement" in the CloudFormation documentation. The `TableName` field on a [DynamoDB Table](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-table.html) is an example of this.
To prevent these from happening, you can add the following 2 fields to your CloudFormation template against the resource you wish to protect:
1. [DeletionPolicy](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html) — Set this to `Retain` to ensure that the resource will not be deleted if it's removed from a stack or if its stack is deleted.
2. [UpdateReplacePolicy](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-updatereplacepolicy.html) — Set this to `Retain` to ensure that if a stack update requires replacing the resource, then the original instance of the resource will be retained even though a new one is created. If this happens and the new resource still uses a same unique field (e.g. name) that the old one had, then the new resource won't be created and the stack update will fail.
My rules of thumb for setting these attributes:
- Apply them to all data-storing resources. This means DynamoDB Tables, S3 Buckets, Cognito User Pools, etc.
- Only use them in non-ephemeral environments, e.g. if you spin up a stack solely for a CI/CD test run and then delete it afterwards, you don't want to leave any resources behind.
Here's a code example of how to enable these settings in `staging` and `prod` stages using the [[Serverless Framework]]:
```yml
# serverless.yml
custom:
deletionPolicy:
default: Delete
staging: Retain
prod: Retain
updateReplacePolicy:
default: Delete
staging: Retain
prod: Retain
resources:
Resources:
MainTable:
Type: AWS::DynamoDB::Table
DeletionPolicy: ${self:custom.deletionPolicy.${self:provider.stage}, '${self:custom.deletionPolicy.default}'}
UpdateReplacePolicy: ${self:custom.updateReplacePolicy.${self:provider.stage}, '${self:custom.updateReplacePolicy.default}'}
```
## Other methods [^fn1]
[^fn1]: [5 Ways to Prevent Accidentally Deleting Your CloudFormation Resources](https://benoitboure.com/5-ways-to-prevent-accidentally-deleting-your-cloudformation-resources) by [[@Benoît Bouré]]
- **Define a Stack Policy** - With Stack Policies, you can constraint what actions are allowed to be executed or not according to specific rules that you define. When you add a policy, _all_ resources are protected by default. You need to explicitly `Allow` the changes on the resources that you want to update. You can think of it as an IAM policy, but the difference here is that it only applies during stack updates.
- **Enable Stack Termination Protection**
- **Place Sensitive Resources in Different Stacks**
Compare & contrast:
![[CloudFormationResourceProtectionComparison.png]]
## References
- [CloudFormation Stack Update Behaviour (official AWS docs)](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks.html)