Cloudformation: Reference Created Subnets in ElastiCache SubnetGroup - amazon-web-services

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"}
]}
]}
}
}
}
}

Related

How to pass list of Refs in CloudFormation JSON?

I used to have a CloudFormation stack which passed VpcId and a List<AWS::EC2::Subnet> (list of subnets). but then I decided I actually want my stack to create its own VPC. So I came up with this:
{
"PublicSubnetOne": {
"Type": "AWS::EC2::Subnet",
"Properties": {
"VpcId": {"Ref": "VPC"},
"CidrBlock": "10.0.0.0/24",
"AvailabilityZone": {
"Fn::Select": ["0", {"Fn::GetAZs": ""}]
}
}
},
"PublicSubnetTwo": {
"Type": "AWS::EC2::Subnet",
"Properties": {
"VpcId": {"Ref": "VPC"},
"CidrBlock": "10.0.0.0/24",
"AvailabilityZone": {
"Fn::Select": ["1", {"Fn::GetAZs": ""}]
}
}
},
"ApplicationLoadBalancer" : {
"Type" : "AWS::ElasticLoadBalancingV2::LoadBalancer",
"Properties" : {
"Subnets" : { "Ref": "PublicSubnetOne,PublicSubnetTwo"}
}
},
}
This results in the below error:
Unresolved resource dependencies [PublicSubnetOne,PublicSubnetTwo] in the Resources block of the template
How to correctly pass a List<> of Refs to a property?
Try this:
{"Ref": "PublicSubnetOne,PublicSubnetTwo"}
[{"Ref": "PublicSubnetOne"} , {"Ref": "PublicSubnetTwo"}]
When you say {"Ref": "PublicSubnetOne,PublicSubnetTwo"}, CloudFormation looks for something with the name verbatim "PublicSubnetOne,PublicSubnetTwo".
You may be confused by this, because it's totally fine to pass a Parameter with --parameter-overrides (from aws cloudformation deploy) with the value "vpc-someidhere,vpc-someidhere2", but it doesn't work that way with refs. I assume CloudFormation formats a comma separated string into a list if a Parameter's Type is List<>
There's also an answer that has similar keywords that actually recommends joining (Fn::Join) two IDs together, but I got an error like Subnets should be List of String with that

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"
}
}
}
}

Using AWS CloudFormation to create a DBSubnetGroup

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"}
]

Create CIDR dynamically in AWS Cloud Formation Template

Below is my AWS cloud formation template for creating VPC and subnets.The VPC is getting created successfully, but not the subnet's. I have tried giving my specific IP range , but it's failing with the error 'The CIDR '172.31.48.0/20' is invalid' How can i create the respective CIDRBlock dynamically in the template using JSON ?
"VPC1": {
"Type": "AWS::EC2::VPC",
"Properties": {
"CidrBlock": "10.10.0.0/16",
"InstanceTenancy": "default",
"EnableDnsSupport": "true",
"EnableDnsHostnames": "false",
"Tags": [
{
"Key": "Name",
"Value": "My Dashboard"
}
]
}
},
"subnet1": {
"Type": "AWS::EC2::Subnet",
"Properties": {
"CidrBlock": "172.31.48.0/20",
"AvailabilityZone": "us-east-2a",
"VpcId": {
"Ref": "VPC1"
}
}
},
"subnet2": {
"Type": "AWS::EC2::Subnet",
"Properties": {
"CidrBlock": "172.31.0.0/20",
"AvailabilityZone": "us-east-2b",
"VpcId": {
"Ref": "VPC1"
},
"Tags": [
{
"Key": "Name",
"Value": "MyDashboard"
}
]
}
},
"subnet3": {
"Type": "AWS::EC2::Subnet",
"Properties": {
"CidrBlock": "172.31.32.0/20",
"AvailabilityZone": "us-east-2a",
"VpcId": {
"Ref": "VPC1"
}
}
}
The VPC is getting created successfully, but not the subnet's. I have tried giving my specific IP range , but it's failing with the error 'The CIDR '172.31.48.0/20' is invalid' How can i create the respective CIDRBlock dynamically in the template using JSON ?
Subnets should be in the same network as the vpc. Trying 10.10.1.0/24 , 10.10.2.0/24 and 10.10.3.0/24 worked for subnets.
got the answer here : https://forums.aws.amazon.com/thread.jspa?messageID=756147#756147