Using AWS CloudFormation to create a DBSubnetGroup - amazon-web-services

I'm using ECS-CLI (0.4.5) to launch a CFN template, and now I'm trying to put an Aurora cluster into the CFN template and update the stack with a changeset through the CFN SDK.
I can't figure out why it's upset about my subnets. The subnets are created by the initial 'ecs-cli up' call. They are in the same vpc as the rest of the stack, they already exist before I try to deploy the changeset, and they are in different availability zones (us-west-2b and us-west-2c).
The only info CFN is giving me is that 'some input subnets are invalid'.
CFN Failure:
Subnets:
I can create a DBSubnetGroup through the management console with the exact same subnets with no problems.
Any ideas on what could be going wrong? Is this a bug in CloudFormation? Let me know if more information is needed to solve this... I'm honestly at such a loss
Here's what my initial template boils down to (It's built into ecs-cli):
"PubSubnetAz1": {
"Type": "AWS::EC2::Subnet",
"Properties": {
"VpcId": {
"Ref": "Vpc"
},
"CidrBlock": "10.0.0.0/24",
"AvailabilityZone": "us-west-2b"
}
},
"PubSubnetAz2": {
"Type": "AWS::EC2::Subnet",
"Properties": {
"VpcId": {
"Ref": "Vpc"
},
"CidrBlock": "10.0.1.0/24",
"AvailabilityZone": "us-west-2c"
}
},
"InternetGateway": {
"Type": "AWS::EC2::InternetGateway"
},
"AttachGateway": {
"Type": "AWS::EC2::VPCGatewayAttachment",
"Properties": {
"VpcId": {
"Ref": "Vpc"
},
"InternetGatewayId": {
"Ref": "InternetGateway"
}
}
},
"RouteViaIgw": {
"Type": "AWS::EC2::RouteTable",
"Properties": {
"VpcId": {
"Ref": "Vpc"
}
}
},
"PublicRouteViaIgw": {
"DependsOn": "AttachGateway",
"Type": "AWS::EC2::Route",
"Properties": {
"RouteTableId": {
"Ref": "RouteViaIgw"
},
"DestinationCidrBlock": "0.0.0.0/0",
"GatewayId": {
"Ref": "InternetGateway"
}
}
},
"PubSubnet1RouteTableAssociation": {
"Type": "AWS::EC2::SubnetRouteTableAssociation",
"Properties": {
"SubnetId": {
"Ref": "PubSubnetAz1"
},
"RouteTableId": {
"Ref": "RouteViaIgw"
}
}
},
"PubSubnet2RouteTableAssociation": {
"Type": "AWS::EC2::SubnetRouteTableAssociation",
"Properties": {
"SubnetId": {
"Ref": "PubSubnetAz2"
},
"RouteTableId": {
"Ref": "RouteViaIgw"
}
}
},
And then when I go to update it, I add this:
"DBSubnetGroup": {
"Type": "AWS::RDS::DBSubnetGroup",
"Properties": {
"DBSubnetGroupDescription": "Aurora Subnet Group using subnets from 2 AZs",
"SubnetIds": {
"Fn::Join": [
",", [{
"Ref": "pubSubnetAz1"
},
{
"Ref": "pubSubnetAz2"
}
]
]
}]
}
}
}
The changeset should be simple enough...
"Changes": [
{
"Type": "Resource",
"ResourceChange": {
"Action": "Add",
"LogicalResourceId": "DBSubnetGroup",
"ResourceType": "AWS::RDS::DBSubnetGroup",
"Scope": [],
"Details": []
}
}
]
I'm using AWSTemplateFormatVersion 2010-09-09 and the JavaScript aws-sdk "^2.7.21"

The issue is that you're concatenating your subnet IDs into a string. Instead, you should pass them in an array. Try this:
"PrivateSubnetGroup": {
"Type": "AWS::RDS::DBSubnetGroup",
"Properties": {
"SubnetIds": [
{
"Ref": "PubSubnetAz1"
},
{
"Ref": "PubSubnetAz2"
}
],
"DBSubnetGroupDescription": "Aurora Subnet Group using subnets from 2 AZs"
}
}
Also, I would highly recommend trying to use yaml instead of json. Cloudformation now supports this natively, along with some shortcut functions to make using references easier, and I think in the long run you'll find it much easier to both read and write.
Here's an example of how you could write equivalent json in yaml:
PrivateSubnetGroup:
Type: AWS::RDS::DBSubnetGroup
Properties:
DBSubnetGroupDescription: Subnet group for Aurora Database
SubnetIds:
- !Ref PubSubnetAz1
- !Ref PubSubnetAz2

According to the AWS::RDS::DBSubnetGroup documentation, the SubnetIDs parameter accepts a List of strings, not a CommaDelimitedList which is what you provided in your example. You should pass the subnets in a JSON array directly, without using Fn::Join:
"SubnetIds": [
{"Ref": "pubSubnetAz1"},
{"Ref": "pubSubnetAz2"}
]

Related

Cloud Formation function doesn't assign value to value for key within a Tag

I have the following Cloud Formation template to create a VPC. The VPC name is generated based off of the region and the environment that the template was created in. The VPC creates without any issues, and running aws cloud formation validate-template --template-url https://foo.template doesn't complaing about any of the syntax.
I would expect the VPC to be named:
vpc-uw1-d-fs
What happens instead is the VPC is left with an empty name and the Name tag has an empty value. Am I not using the function correctly? If I remove the Fn::FindInMap function usage, I get the name generated - it's just missing the environment mapped value.
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "VPC for a new environment to use.",
"Parameters": {
"EnvironmentName": {
"Description": "Name of the environment that this VPC will be used for.",
"Type": "String",
"MinLength": "2",
"MaxLength": "20",
"AllowedPattern": "[a-zA-Z]*",
"AllowedValues": [
"Development",
"QA",
"Test",
"Production"
]
}
},
"Resources": {
"VPC": {
"Type": "AWS::EC2::VPC",
"Properties": {
"CidrBlock": "10.0.0.0/16",
"EnableDnsSupport": false,
"EnableDnsHostnames": false,
"InstanceTenancy": "default",
"Tags": [ {
"Key": "Name",
"Value": {
"Fn::Join": [
"-",
[
"vpc",
{ "Ref": "AWS::Region" },
{ "Fn::FindInMap": [
"EnvironmentMap", { "Ref": "EnvironmentName" }, "AbbreviatedName"
]},
"fs"
]
]
}
}]
}
}
},
"Mappings": {
"RegionMap": {
"us-east-1": {
"regionName": "ue1"
},
"us-west-1": {
"regionName": "uw1"
}
},
"EnvironmentMap": {
"Development": {
"AbbreviatedName": "d"
},
"QA": {
"AbbreviatedName": "qa"
},
"Test": {
"AbbreviatedName": "t"
},
"Production": {
"AbbreviatedName": "p"
}
}
},
"Outputs": {
}
}
Your template is working perfectly fine for me.
I ran it in the ap-southeast-2 region and it produced the tag:
Name: vpc-ap-southeast-2-d-fs
(The RegionMap is not used in the template given.)

AWS - Merge cloudformation stack with a VPC

I am trying to create a stack in AWS CloudFormation, My template basically consists of Ec2 instance, RDS instance for DB (MySQL engine) and a S3 bucket. but, its throwing error stating (db.t2.micro) this DB instance class cannot be created without a VPC, then I changed the DB instance class to (db.m1.small) again am getting same error. I even created a VPC too, but not sure how do I create my stack within the VPC which I created. I work in my company's AWS account. where already few other VPCs are available.
Thanks in advance :)
Modified the JSON script after getting answers. This script is in working condition and could create stack. TESTED!
Updated Code
{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"DBSubnetGroup": {
"Type": "AWS::RDS::DBSubnetGroup",
"Properties": {
"DBSubnetGroupDescription": "This subnet belongs to Abdul's VPC",
"DBSubnetGroupName": "somename",
"SubnetIds": [
"subnet-f6b15491",
"subnet-b154569e"
]
}
},
"DB": {
"Type": "AWS::RDS::DBInstance",
"Properties": {
"AllocatedStorage": "5",
"StorageType": "gp2",
"DBInstanceClass": "db.m1.small",
"DBName": "wordpress",
"Engine": "MySQL",
"MasterUsername": "wordpress",
"MasterUserPassword": "Word12345",
"DBSubnetGroupName": {
"Ref": "DBSubnetGroup"
}
}
},
"EC2": {
"Type": "AWS::EC2::Instance",
"Properties": {
"ImageId": "ami-c481fad3",
"InstanceType": "t2.micro",
"SubnetId": "subnet-b154569e"
}
},
"S3": {
"Type": "AWS::S3::Bucket",
"Properties": {
"BucketName": "wp-abdultestbuck"
}
}
}
}
You need to create an AWS::RDS::DBSubnetGroup and then reference in the AWS::RDS::DBInstance
{
"Resources": {
"DBSubnetGroup": {
"Type": "AWS::RDS::DBSubnetGroup",
"Properties": {
"DBSubnetGroupDescription": "",
"SubnetIds": [ "<Subnet ID 1","<Subnet ID 2>" ],
}
},
"DB": {
"Type": "AWS::RDS::DBInstance",
"Properties": {
....
"DBSubnetGroupName": { "Ref": "DBSubnetGroup" }
}
},
"EC2": {
"Type": "AWS::EC2::Instance",
"Properties": {
"ImageId": "ami-c481fad3",
"InstanceType": "t2.micro",
"SubnetId": "<SubnetID>"
}
}
}
}

nested cloudformation get vpcid on vpc creation

Following several threads on SO and aws forums, I am trying to get a basic nested cloudformation example working.
The ChildStack01 creates a VPC, then ChildStack02 adds a subnet. but after trying several combinations I get the same type of error, Output 'VpcID' not found in stack
{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"ChildStack01": {
"Type": "AWS::CloudFormation::Stack",
"Properties": {
"TemplateURL": "https://s3-eu-west-1.amazonaws.com/cf-templates-1u1ziwem31f87-eu-west-1/xxx",
"TimeoutInMinutes": "60"
}
},
"ChildStack02": {
"Type": "AWS::CloudFormation::Stack",
"Properties": {
"TemplateURL": "https://s3-eu-west-1.amazonaws.com/cf-templates-1u1ziwem31f87-eu-west-1/yyy",
"Parameters": {
"VpcId" : { "Fn::GetAtt" : [ "ChildStack01", "Outputs.VpcID" ] }
},
"TimeoutInMinutes": "60"
}
}
}
I have tried adding a parameter with
"VPC" : {
"Description" : "VPC ID",
"Type": "AWS::EC2::VPC::Id"
}
but then generates an error as there is no reference value for VPC listed in http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-ref.html
Is there a basic way to get the VPC-id after it has been created?
thanks
Art
You should look at Exports.
In your VPC stack, create an Export section:
Outputs:
VpcId:
Value: !Ref VPC
Export:
Name: Unique-VpcId
You can then import that value in another stack:
VpcId:
Fn::ImportValue: Unique-VpcId
You should of course include some way of generating unique export names (they have to be unique within a region) rather than hard-coding as in my example.
In CF Template create VPC, use as below:
"Resources": {
"VPC": {
"Type": "AWS::EC2::VPC",
"Properties": {
....
}
}
},
"Outputs": {
"VPC": {
"Description": "VPC",
"Value": {
"Ref": "VPC"
},
"Export": {
"Name": {
"Fn::Sub": "${AWS::StackName}-VPC"
}
}
}
}
In CF Template that you want to use output of 1st CF Template, as:
"Parameters": {
"VPCStackName": {
"Description": "Name of VPC CF Stack",
"Type": "String",
"Default": "SOME_NAME"
}
},
"Resources": {
"Subnet1": {
"Type": "AWS::EC2::Subnet",
"Properties": {
"VpcId": {
"Fn::ImportValue": {
"Fn::Sub": "${VPCStackName}-VPC"
}
},
"CidrBlock": {
"Ref": "CidrBlockSubnet1"
},
"AvailabilityZone": {
"Ref": "AZ_NAME"
}
}
}
}

Creating a Cloudformation DBCluster from a snapshot

We are currently having trouble recreating our stack in CloudFormation. We basically want to create a new Aurora cluster from a snapshot, and attach a DB instance to it.
I have successfully created a DBCluster from a snapshot using the SnapshotIdentifier value, but this does not create an instance inside the cluster. If I then add a DBInstance to the cluster in cloudformation using DBClusterIdentifier it says that it needs the MasterUsername and MasterUserPassword values. But my understanding is that these are part of the snapshot.
I feel like I am missing something very fundemental with the aurora CloudFormation stuff.
This is out DBCluster and DBInstance properties:
"DBCluster": {
"Type": "AWS::RDS::DBCluster",
"Properties": {
"Port": "3306",
"Engine": "aurora",
"EngineVersion": "5.6.10a",
"SnapshotIdentifier": {
"Ref": "MainSnapshotIdenifer"
},
"DBSubnetGroupName": {
"Ref": "DatabaseSubnetGroup"
}
}
},
"DBInstance": {
"Type": "AWS::RDS::DBInstance",
"Properties": {
"DBClusterIdentifier": {
"Ref": "DBCluster"
},
"DBInstanceIdentifier": "HanzsDBInstance",
"DBInstanceClass": "db.r3.large",
"Engine": "aurora",
"DBParameterGroupName": {
"Ref": "DBParameterGroup"
},
"PubliclyAccessible": "true",
"DBSubnetGroupName": {
"Ref": "DatabaseSubnetGroup"
},
"DBSecurityGroups": [
{
"Ref": "DatabaseSecurityGroup"
}
]
}
}

Cloudformation: Reference Created Subnets in ElastiCache SubnetGroup

I'm having a challenge creating an ElastiCache SubnetGroup which dynamically references the correct subnets. I'd like to use the same template in both the east and west region so I'm specifying the subnets for the subnet group in mappings. However, when I attempt to run update my stack, I get the following error:
Value of property SubnetIds must be of type List of String
Here's a gist showing roughly what i am trying to do: https://gist.github.com/brockhaywood/b71ed34c6a554a0a0fec
This unanswered question on the AWS forums appears to be a very similar problem: https://forums.aws.amazon.com/message.jspa?messageID=532454
I think SubnetIds should be an array, where you have a single object.
"ElastiCacheSubnetGroup": {
"Type": "AWS::ElastiCache::SubnetGroup",
"Properties": {
"SubnetIds": [
{
"Fn::FindInMap":["RegionMap", { "Ref":"AWS::Region" }, AppSubnets" ]
}
]
}
}
The specific issue is that you can't use Ref within a Mappings value, as noted in the Mappings documentation:
You cannot include parameters, pseudo parameters, or intrinsic functions in the Mappings section.
As an alternative, you can use Conditions to accomplish what your template is attempting. Here's a complete working example:
{
"Description": "Create an ElastiCache SubnetGroup with different subnet depending on the current AWS region."
"Conditions": {
"us-east-1": {"Fn::Equals": [{"Ref":"AWS::Region"}, "us-east-1"]},
"us-west-2": {"Fn::Equals": [{"Ref":"AWS::Region"}, "us-west-2"]}
},
"Resources": {
"VPC": {
"Type": "AWS::EC2::VPC",
"Properties": {
"CidrBlock": "10.0.0.0/16"
}
},
"AppSubnetA": {
"Type": "AWS::EC2::Subnet",
"Properties": {
"VpcId": {"Ref": "VPC"},
"CidrBlock": "10.0.0.0/24",
"AvailabilityZone": {"Fn::Select": [1, {"Fn::GetAZs": ""}]}
}
},
"AppSubnetB": {
"Type": "AWS::EC2::Subnet",
"Properties": {
"VpcId": {"Ref": "VPC"},
"CidrBlock": "10.0.1.0/24",
"AvailabilityZone": {"Fn::Select": [1, {"Fn::GetAZs": ""}]}
}
},
"ElastiCacheSubnetGroup": {
"Type": "AWS::ElastiCache::SubnetGroup",
"Properties": {
"Description": "SubnetGroup",
"SubnetIds": {"Fn::If": ["us-east-1", [
{"Ref": "AppSubnetA"}
],
{"Fn::If": ["us-west-2",
[
{"Ref": "AppSubnetB"}
],
{"Ref":"AWS::NoValue"}
]}
]}
}
}
}
}