nested cloudformation get vpcid on vpc creation - amazon-web-services

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

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

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

CloudFormation, passing a List<AWS::EC2::Subnet::Id> parameter as a comma separated string?

How can I pass a parameters of type List<AWS::EC2::Subnet::Id> as a comma separated string?
I have the following template:
{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {
"PrivateSubnets": {
"Description": "The private subnets in which Beanstalk EC2 instances will created",
"Type": "List<AWS::EC2::Subnet::Id>"
},
"PublicSubnets": {
"Description": "The public subnets in which the Beanstalk ELBs will be created",
"Type": "List<AWS::EC2::Subnet::Id>"
}
},
"Resources": {
"MyApp": {
"Type": "AWS::ElasticBeanstalk::Application",
"Properties": {
"ApplicationName": "MyApp",
"Description": "AWS Elastic Beanstalk Application"
}
},
"ConfigTemplate": {
"Type": "AWS::ElasticBeanstalk::ConfigurationTemplate",
"Properties": {
"ApplicationName": { "Ref": "MyApp" },
"Description": "Microsite Beanstalk config template",
"OptionSettings": [
{ "Namespace": "aws:ec2:vpc", "OptionName": "ELBSubnets", "Value": { "Ref": "PublicSubnets" } },
{ "Namespace": "aws:ec2:vpc", "OptionName": "Subnets", "Value": { "Ref": "PrivateSubnets"} }
],
"SolutionStackName": "64bit Amazon Linux 2016.03 v2.1.7 running PHP 5.6"
}
}
}
}
When I attempt to create the stack, I get the following error:
CREATE_FAILED AWS::ElasticBeanstalk::ConfigurationTemplate ConfigTemplate Value of property Value must be of type String
An attempt to use Fn:Join to write the content of the private and public subnets as comma separated strings, e.g.
{ "Namespace": "aws:ec2:vpc", "OptionName": "ELBSubnets", "Value": { "Fn:Join": [",", { "Ref": "PublicSubnets" }]} },
{ "Namespace": "aws:ec2:vpc", "OptionName": "Subnets", "Value": { "Fn:Join": [",", { "Ref": "PrivateSubnets"}]} },
result in
Template validation error: Template Error: Encountered unsupported function: Fn:Join Supported functions are: [Fn::Base64, Fn::GetAtt, Fn::GetAZs, Fn::ImportValue, Fn::Join, Fn::FindInMap, Fn::Select, Ref, Fn::Equals, Fn::If, Fn::Not, Condition, Fn::And, Fn::Or, Fn::Contains, Fn::EachMemberEquals, Fn::EachMemberIn, Fn::ValueOf, Fn::ValueOfAll, Fn::RefAll, Fn::Sub]
According to http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/command-options-general.html#command-options-general-ec2vpc you need to provide a comma delimited list. So use Fn::Join (note the two colons)

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