AWS CloudFormation creates new RDS instance

amazon-cloudformationamazon-web-services

I've created a CloudFormation script that stands up a clone of our existing AWS stack. I've added in information so that CloudFormation will create RDS from either a snapshot of an existing RDS instance, or create a new instance with a named database. However, when I try to apply this same script to our existing stack (verified by creating a new stack with the script from the existing stack and then attempting to upgrade via the new script), CloudFormation always creates a new RDS instance.

Extracted portions of the CloudFormation script are below.

{
    "Parameters": {
        "liveDbName" : {
            "Default": "default",
                "Description" : "The live database name to create by default",
                "Type": "String",
                "MinLength": "1",
                "MaxLength": "64",
                "AllowedPattern" : "[a-zA-Z][a-zA-Z0-9]*",
                "ConstraintDescription" : "must begin with a letter and contain only alphanumeric characters."
        },
        "liveDbSnapshotIdentifier" : {
            "Description" : "This overrides Default Production Db name",
            "Type": "String",
            "Default": ""
        },
    },
    "Metadata": {
        "AWS::CloudFormation::Interface": {
            "ParameterGroups": [
                {
                    "Label": {
                        "default": "Db Layer Configuration"
                    },
                    "Parameters": [
                        "webDbInstanceType",
                        "liveDbName",
                        "liveDbSnapshotIdentifier",
                        "dbTimeZone",
                        "dbMasterUser",
                        "dbMasterPassword"
                    ]
                }
            ]
        }
    },
    "Conditions": {
        "UseLiveDbSnapshot" : { "Fn::Not" : [{ "Fn::Equals" : [ {"Ref" : "liveDbSnapshotIdentifier"}, "" ] }] },
    }
    "Resources": {
        "WebDb": {
            "Type": "AWS::RDS::DBInstance",
            "DeletionPolicy": "Snapshot",
            "Properties": {
                "AllocatedStorage": "100",
                "AutoMinorVersionUpgrade": "true",
                "BackupRetentionPeriod": "30",
                "CopyTagsToSnapshot": "true",
                "DBName" : {
                    "Fn::If" : [ "UseLiveDbSnapshot", { "Ref" : "AWS::NoValue"}, { "Ref" : "liveDbName" } ]
                },
                "DBSnapshotIdentifier" : {
                    "Fn::If" : [ "UseLiveDbSnapshot", { "Ref" : "liveDbSnapshotIdentifier" }, { "Ref" : "AWS::NoValue"} ]
                },
                "DBInstanceClass": {
                    "Ref": "webDbInstanceType"
                },
                "DBParameterGroupName": {
                    "Ref": "WebDbParameterGroup"
                },
                "DBSubnetGroupName": {
                    "Ref": "DbSubnetGroup"
                },
                "Engine": "mysql",
                "MasterUsername": {
                    "Ref": "dbMasterUser"
                },
                "MasterUserPassword": {
                    "Ref": "dbMasterPassword"
                },
                "MultiAZ": "true",
                "PubliclyAccessible": "false",
                "StorageType": "gp2",
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": "WebDb"
                    }
                ]
            }
        }
    }
}

There are of course other portions of the script, but this is the portion (fully "namespaced," I believe) that deals with our database section.

What am I doing wrong with my script, and is there a correct way to do so? Obviously I don't want CloudFormation to restore a snapshot over our existing instance, but I don't want it to create a new instance with the named database, either.

EDIT: "Existing" stack script included

I've added the existing stack script as a link to Dropbox because the file is too long to include here directly: https://www.dropbox.com/s/313kmcnzk0pvyqi/sanitized-cloudformation.json?dl=0

Best Answer

Your original CloudFormation template did not include DBName or DBSnapshotIdentifier properties. So the RDS instance was created without a DBName. Any database housed by your RDS instance was created after-the-fact, and not by CloudFormation.

Your new template includes either the DBName or DBSnapshotIdentifier, depending on the input parameters.

According to the CloudFormation reference docs for the AWS::RDS::DBInstance resource, if you add/change/remove either of the DBName or DBSnapshotIdentifier properties, then the RDS instance will be re-created.

Source: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-rds-database-instance.html

I'm guessing that when you're applying the updated template, you're attempting to use the name of your database that you have inside your RDS instance as the value for liveDbName. However, as far as CloudFormation is concerned, this is a change to the RDS instance and requires replacement.

To apply the template update, you'll need to modify it such that neither DBName nor DBSnapshotIdentifier are applied.

Related Topic