Creating a Cloudformation DBCluster from a snapshot - amazon-web-services

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

Related

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

Ansible Cloudformation, how to not break if Resource already exists?

I have the following AWS Cloudformation config, which sets up S3, Repositories.
When I run it via an ansible playbook, on the second time running the playbook this happens
AWS::ECR::Repository Repository CREATE_FAILED: production-app-name already exists
etc
How can I make it so that when this is ran multiple times, it will keep the existing s3 and repository instead of just blowing up? (I had assumed the param "DeletionPolicy": "Retain", would do this)
What I'd like to achieve:
If i run this 100x, I want the same resource state as it was after run #1. I do not want any resources deleted/wiped of any data.
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Pre-reqs for Elastic Beanstalk application",
"Parameters": {
"BucketName": {
"Type": "String",
"Description": "S3 Bucket name"
},
"RepositoryName": {
"Type": "String",
"Description": "ECR Repository name"
}
},
"Resources": {
"Bucket": {
"Type": "AWS::S3::Bucket",
"DeletionPolicy": "Retain",
"Properties": {
"BucketName": { "Fn::Join": [ "-", [
{ "Ref": "BucketName" },
{ "Ref": "AWS::Region" }
]]}
}
},
"Repository": {
"Type": "AWS::ECR::Repository",
"DeletionPolicy": "Retain",
"Properties": {
"RepositoryName": { "Ref": "RepositoryName" }
}
}
},
"Outputs": {
"S3Bucket": {
"Description": "Full S3 Bucket name",
"Value": { "Ref": "Bucket" }
},
"Repository": {
"Description": "ECR Repo",
"Value": { "Fn::Join": [ "/", [
{
"Fn::Join": [ ".", [
{ "Ref": "AWS::AccountId" },
"dkr",
"ecr",
{ "Ref": "AWS::Region" },
"amazonaws.com"
]]
},
{ "Ref": "Repository" }
]]}
}
}
}
edit:
DB with similar issue when ran twice
{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {
"DBPassword": {
"MinLength": "8",
"NoEcho": true,
"Type": "String"
},
"Environment": {
"MinLength": "1",
"Type": "String"
},
"DBName": {
"Type": "String",
"Description": "DBName"
},
"DBInstanceIdentifier": {
"Type": "String",
"Description": "DBInstanceIdentifier"
},
"DBPort": {
"Type": "String",
"Description": "DBPort"
},
"DBUsername": {
"Type": "String",
"Description": "DBName"
}
},
"Outputs": {
"Url": {
"Value": {
"Fn::Sub": "postgres://${DBUsername}:${DBPassword}#${Instance.Endpoint.Address}:${Instance.Endpoint.Port}/${DBName}"
}
}
},
"Resources": {
"Instance": {
"Type": "AWS::RDS::DBInstance",
"DeletionPolicy": "Retain",
"Properties": {
"AllocatedStorage": "10",
"DBInstanceClass": "db.t2.micro",
"DBInstanceIdentifier": {"Ref": "DBInstanceIdentifier"},
"DBName": {
"Ref": "DBName"
},
"Engine": "postgres",
"EngineVersion": "9.6.6",
"MasterUsername": {
"Ref": "DBUsername"
},
"MasterUserPassword": {
"Ref": "DBPassword"
},
"MultiAZ": "false",
"Port": {
"Ref": "DBPort"
},
"PubliclyAccessible": "false",
"StorageType": "gp2"
}
}
}
}
The field RepositoryName in AWS::ECR::Repository is actually not required and I would advise against specifying one. By letting CloudFormation dynamically assign a unique name to the repository you'll avoid collision.
If you later want to use the repository name, for exemple: in a task definition, you can use the "Ref" function like so { "Ref": "Repository" } to extract the unique name generated by CloudFormation.
As for the issue with the RDS instance, tt comes down to the same problem of hardcoding resources name.
Using retain will keep the resource alive but it will no longer be managed by CloudFormation which is a big problem.
Just make sure when doing updates to never modify a parameter that require a resource "replacement". The documentation always states what kind of update a parameter change will incur.
Image taken from (here)
If you really need to change a parameter that requires a replacement. Create a new resource with the adapter parameters, migrate whatever data you had in the database or ECR repository, then remove the old resource from the template. If you don't need to migrate anything, make sure you don't have hardcoded names and let CloudFormation perform the replacement.

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

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