CloudFormation Annoyance: Deletion Policy as a Parameter

You can create some high value resources using CloudFormation that you’d like to ensure exist even after a stack has been removed. Imagine being the admin to accidently delete the wrong stack and having to watch as your RDS master, and all your prod data, slowly vanishes in to the void of AWS reclaimed volumes. Luckily AWS provides a way to reduce this risk, the DeletionPolicy Attribute. By specifying this on a resource you can ensure that if your stack is deleted then certain resources survive and function as usual. This also helps keep down the number of stacks you have in the “DELETE_FAILED” stage if you try and remove a shared security group or such.

    "Resources": {

      "TestBucket": {
        "DeletionPolicy": "Retain",
        "Type": "AWS::S3::Bucket",
        "Properties": {
          "BucketName": "MyTestBucketOf54321SemiRandomName"
        }
      }

    }

Once you start sprinkling this attribute through your templates you’ll probably feel the need to have it vary in staging and prod. While it’s a lovely warm feeling to have your RDS masters in prod be a little harder to accidently kill you’ll want a clean tear down of any frequently created staging or developer stacks for example. The easiest way to do this is to make the DeletionPolicy take its value from a parameter, probably using code like that below.

    {
      "AWSTemplateFormatVersion": "2010-09-09",
      "Description" : "Retain on delete test template",

      "Parameters" : {

        "RetainParam": {
          "Type": "String",
          "AllowedValues": [ "Retain", "Delete", "Snapshot" ],
          "Default": "Delete"
        }

      },
      "Resources": {

        "TestBucket": {
          "DeletionPolicy": { "Ref" : "RetainParam" },
          "Type": "AWS::S3::Bucket",
          "Properties": {
            "BucketName": "MyTestBucketOf54321SemiRandomName"
          }
        }

      }
    }

Unfortunately this doesn’t work. You’ll get an error that looks something like cfn-validate-template: Malformed input-Template format error: Every DeletionPolicy member must be a string. if you try to validate your template (and we always do that, right?).

There are a couple of ways around this, the two I’ve used are: templating your CloudFormation json from a tool higher up in your stack, Ansible for example. The downside is your templates are unrunable without expansion. A second approach is to double up on some resource declarations and use CloudFormation Conditionals. You can then create the same resource, with the DeletionPolicy set to the appropriate value, based off the value of a parameter. I’m uncomfortable using this in case of resource removal on stack updates if the wrong parameters are ever passed to your stack. I prefer the first option.

Even though there are ways to work around this limitation it really feels like it’s something that’ Should Just Work’ and as a CloudFormation user I’ll be a lot happier when it does.