aws CloudFormation AWS::EC2::Instance BlockDeviceMappings and Volumes - amazon-web-services

I am sort of confused about two AWS::EC2::Instance properties:
BlockDeviceMappings and Volumes.
I have read documentation a number of times but still don't really understand the difference.
Here is my template:
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Description" : "kappoowTest",
"Mappings" : {
"AmazonLinuxAMI" : {
"eu-west-1" :
{ "AMI" : "ami-d8f9f1ac" },
"us-west-1" :
{ "AMI" : "ami-b63210f3" }
}
},
"Resources" : {
"SomeInstance" :{
"Type" : "AWS::EC2::Instance",
"Properties" : {
"AvailabilityZone" : "eu-west-1a",
"BlockDeviceMappings" : [
{
"DeviceName" : "/dev/sdc",
"Ebs" : { "VolumeSize" : "50" }
},
{
"DeviceName" : "/dev/sdd",
"Ebs" : { "VolumeSize" : "100" }
}
],
"DisableApiTermination" : "true",
"EbsOptimized" : "true",
"ImageId" : { "Fn::FindInMap" : [ "AmazonLinuxAMI", { "Ref" : "AWS::Region" }, "AMI" ]},
"InstanceType" : "m1.large",
"KeyName" : "mongo_test",
"Monitoring" : "true",
"SecurityGroups" : [ "default" ],
"Volumes" : [
{ "VolumeId" : { "Ref" : "NewVolume" }, "Device" : "/dev/sdk" }
]
}
},
"NewVolume" : {
"Type" : "AWS::EC2::Volume",
"Properties" : {
"Size" : "100",
"AvailabilityZone" : "eu-west-1a"
}
}
}}
Here I have created 3 volumes. 2 with
"BlockDeviceMappings" : [
{
"DeviceName" : "/dev/sdc",
"Ebs" : { "VolumeSize" : "50" }
},
{
"DeviceName" : "/dev/sdd",
"Ebs" : { "VolumeSize" : "100" }
}
]
and another one with:
"Volumes" : [
{ "VolumeId" :
{ "Ref" : "NewVolume" }, "Device" : "/dev/sdk" }
]
CloudFormation ran fine, but I fail to see the difference.
Could someone tell me what which way is better of adding EBS volumes to EC2 instance and what is the difference between these two methods ?

With BlockDeviceMappings you can mount ephemeral storage not only ebs.
Volumes is only ebs volumes, and provides better options (like choosing the AZ, or specifying the IOPs if you want to use PIOPs).
If all what you want is simple ebs volumes, then there is no difference.

Related

Is there a way to configure the 'Stack Name' of nested stacks in AWS cloudformation ?

I am trying to create a nested stack using AWS CloudFormation.
I need to specify the 'Stack Name' of the nested stack.
I tried using the 'Tags' property with Key: 'Stack Name'. But that didn't help.
Is there any way to provide the Stack Name as an input while creating nested stack?
In a given template, you have access to the pseudo-parameters stackId and stackName. Those can be marked for export, then referenced in another stack. Joined, they give you the child stack's name.
I found the following video quite useful despite its age: https://youtu.be/6R44BADNJA8
This sample template may also be helpful in understanding how these work: https://s3.amazonaws.com/cloudformation-examples/user-guide/cross-stack/SampleNetworkCrossStack.template
{ "AWSTemplateFormatVersion" : "2010-09-09", "Description" : "AWS CloudFormation Sample Template VPC_with_PublicIPs_And_DNS: Sample template that creates a VPC with DNS and public IPs enabled. Note that you are billed for the AWS resources that you use when you create a stack from this template.", "Resources" : { "VPC" : { "Type" : "AWS::EC2::VPC", "Properties" : { "EnableDnsSupport" : "true", "EnableDnsHostnames" : "true", "CidrBlock" : "10.0.0.0/16" } }, "PublicSubnet" : { "Type" : "AWS::EC2::Subnet", "Properties" : { "VpcId" : { "Ref" : "VPC" }, "CidrBlock" : "10.0.0.0/24" } }, "InternetGateway" : { "Type" : "AWS::EC2::InternetGateway" }, "VPCGatewayAttachment" : { "Type" : "AWS::EC2::VPCGatewayAttachment", "Properties" : { "VpcId" : { "Ref" : "VPC" }, "InternetGatewayId" : { "Ref" : "InternetGateway" } } }, "PublicRouteTable" : { "Type" : "AWS::EC2::RouteTable", "Properties" : { "VpcId" : { "Ref" : "VPC" } } }, "PublicRoute" : { "Type" : "AWS::EC2::Route", "DependsOn" : "VPCGatewayAttachment", "Properties" : { "RouteTableId" : { "Ref" : "PublicRouteTable" }, "DestinationCidrBlock" : "0.0.0.0/0", "GatewayId" : { "Ref" : "InternetGateway" } } }, "PublicSubnetRouteTableAssociation" : { "Type" : "AWS::EC2::SubnetRouteTableAssociation", "Properties" : { "SubnetId" : { "Ref" : "PublicSubnet" }, "RouteTableId" : { "Ref" : "PublicRouteTable" } } }, "PublicSubnetNetworkAclAssociation" : { "Type" : "AWS::EC2::SubnetNetworkAclAssociation", "Properties" : { "SubnetId" : { "Ref" : "PublicSubnet" }, "NetworkAclId" : { "Fn::GetAtt" : ["VPC", "DefaultNetworkAcl"] } } }, "WebServerSecurityGroup" : { "Type" : "AWS::EC2::SecurityGroup", "Properties" : { "GroupDescription" : "Enable HTTP ingress", "VpcId" : { "Ref" : "VPC" }, "SecurityGroupIngress" : [ { "IpProtocol" : "tcp", "FromPort" : "80", "ToPort" : "80", "CidrIp" : "0.0.0.0/0" } ] } } }, "Outputs" : { "VPCId" : { "Description" : "VPC ID", "Value" : { "Ref" : "VPC" }, "Export" : { "Name" : {"Fn::Sub": "${AWS::StackName}-VPCID" }} }, "PublicSubnet" : { "Description" : "The subnet ID to use for public web servers", "Value" : { "Ref" : "PublicSubnet" }, "Export" : { "Name" : {"Fn::Sub": "${AWS::StackName}-SubnetID" }} }, "WebServerSecurityGroup" : { "Description" : "The security group ID to use for public web servers", "Value" : { "Fn::GetAtt" : ["WebServerSecurityGroup", "GroupId"] }, "Export" : { "Name" : {"Fn::Sub": "${AWS::StackName}-SecurityGroupID" }} } } }
As commented below, the answer is no for now.
Original:Yes, it's the resource name that gets taken as the stack name. Below you'd get a stack called myStackName
YAML
AWSTemplateFormatVersion: "2010-09-09"
Resources:
myStackName:
Type: "AWS::CloudFormation::Stack"
Properties:
TemplateURL: "https://s3.amazonaws.com/cloudformation-templates-us-east-1/EC2ChooseAMI.template"
Parameters:
InstanceType: "t1.micro"
KeyName: "mykey"
JSON
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Resources" : {
"myStackName" : {
"Type" : "AWS::CloudFormation::Stack",
"Properties" : {
"TemplateURL" : "https://s3.amazonaws.com/cloudformation-templates-us-east-1/EC2ChooseAMI.template",
"Parameters" : {
"InstanceType" : "t1.micro",
"KeyName" : "mykey"
}
}
}
}
}

cloudformation failing with security group mismatch

I'm trying to setup my cloudformation for my database:
"VPC" : {
"Type" : "AWS::EC2::VPC",
"Properties" : {
"CidrBlock" : "10.0.0.0/16",
"EnableDnsSupport" : "false",
"EnableDnsHostnames" : "false",
"InstanceTenancy" : "default",
"Tags" : [ { "Key" : "Name", "Value" : "DomainName" } ]
}
},
"Subnet" : {
"Type" : "AWS::EC2::Subnet",
"Properties" : {
"VpcId" : { "Ref" : "VPC" },
"CidrBlock" : "10.0.0.0/16",
"AvailabilityZone" : { "Fn::Select": [ "0", { "Fn::GetAZs" : { "Ref" : "AWS::Region" } }]},
"Tags" : [ { "Key" : "Name", "Value" : "DomainName" } ]
}
},
"SecurityGroup" : {
"Type" : "AWS::EC2::SecurityGroup",
"Properties" : {
"GroupDescription" : "Allow http to client host",
"VpcId" : {"Ref" : "VPC"},
"SecurityGroupIngress" : [{
"IpProtocol" : "tcp",
"FromPort" : "3306",
"ToPort" : "3306",
"CidrIp" : "10.0.0.0/16"
}],
"Tags" : [ { "Key" : "Name", "Value" : "DomainName" } ]
}
},
"Database" : {
"Type" : "AWS::RDS::DBInstance",
"Properties" : {
"DBName" : { "Fn::Join": ["", { "Fn::Split": [".", { "Ref" : "DomainName" }]}]},
"AllocatedStorage" : "5",
"DBInstanceClass" : "db.t2.micro",
"Engine" : "MySQL",
"EngineVersion" : "5.5",
"MasterUsername" : { "Ref": "DBUsername" },
"MasterUserPassword" : { "Ref": "DBPassword" },
"VPCSecurityGroups" : [ { "Fn::GetAtt": [ "SecurityGroup", "GroupId" ] } ],
"Tags" : [ { "Key" : "Name", "Value" : "DomainName" } ]
},
"DeletionPolicy" : "Snapshot"
},
Should be setting up a VPC for the database. But when I run the cloudformation template I get the following error:
UPDATE_FAILED AWS::RDS::DBInstance Database Database is in vpc-3081245b, but Ec2 Security Group sg-b122ffca is in vpc-f7173290
How do I get my database in the VPC properly?
As part of your Database definition, you can specify a DBSubnetGroupName.
A DB Subnet Group provides a list of subnets in which the Database is allowed to run. Each subnet in a DB Subnet Group belongs to a VPC.
Therefore, you need to do the following to your Amazon CloudFormation template:
Add a AWS::RDS::DBSubnetGroup, specifying the Subnet already defined in your template
Add a DBSubnetGroupName parameter to your AWS::RDS::DBInstance definition

How to add a RDS instance to a VPC using aws cloudformation

When I launch a RDS instance manually I'm able to assign what VPC I want it to be part of. I'm trying to create a stack using AWS cloudformation, however I do not see an API to be able to do that. I can create my VPC in the stack and then reference it for security groups both EC2 and DB security groups and they both end up been part of the VPC however the RDS instance itself does not. Is there a way to assign the VPC to the RDS instance?
Below is my template:
{
"AWSTemplateFormatVersion": "2010-09-09",
"Metadata": {
"AWS::CloudFormation::Designer": {
"30e03bfc-b61a-4d6c-89db-1b62b258a305": {
"size": {
"width": 80,
"height": 80
},
"position": {
"x": 700,
"y": 170
},
"z": 0,
"embeds": []
}
}
},
"Parameters": {
"DBPreferredBkupWindow": {
"Description" : "The daily time range (in UTC) during which automated backups are created, ideally off peak-hours.",
"Type" : "String",
"MinLength" : "1",
"MaxLength" : "11",
"AllowedPattern" : "\\d[0-23]:\\d[0-59]-\\d[0-23]:\\d[0-59]",
"Default" : "01:00-02:00"
}
},
"Resources": {
"VPC": {
"Type": "AWS::EC2::VPC",
"Properties": {
"CidrBlock" : "172.16.0.0/16",
"EnableDnsSupport" : true
}
},
"DB": {
"Type": "AWS::RDS::DBInstance",
"Properties": {
"DBName" : "ems",
"Engine" : "postgres",
"EngineVersion" : "9.4.7",
"DBInstanceClass" : "db.t1.micro",
"DBInstanceIdentifier" : "rltdb",
"MasterUsername" : "pgadmin",
"MasterUserPassword" : "pgadmin1",
"AllocatedStorage" : "100",
"Iops" : "1000",
"BackupRetentionPeriod" : "7",
"PreferredBackupWindow" : { "Ref" : "DBPreferredBkupWindow" },
"MultiAZ" : true,
"PubliclyAccessible" : false,
"AutoMinorVersionUpgrade" : false,
"VPCSecurityGroups" : [{ "Ref" : "SecurityGroup" } ]
},
"Metadata": {
"AWS::CloudFormation::Designer": {
"id": "30e03bfc-b61a-4d6c-89db-1b62b258a305"
}
}
},
"DBSecurityGroup": {
"Type": "AWS::RDS::DBSecurityGroup",
"Properties": {
"EC2VpcId" : { "Ref" : "VPC" },
"DBSecurityGroupIngress" : { "EC2SecurityGroupName": { "Ref": "SecurityGroup"} },
"GroupDescription" : "Database Access"
}
},
"SecurityGroup" : {
"Type" : "AWS::EC2::SecurityGroup",
"Properties" : {
"VpcId" : { "Ref" : "VPC" },
"GroupDescription" : "Enable database access for application",
"SecurityGroupIngress" : [
{"IpProtocol" : "tcp", "FromPort" : "5432", "ToPort" : "5432", "CidrIp" : "0.0.0.0/0"}
]
}
}
}
}
You have to create a DBSubnetGroup and at least two subnets in your CloudFormation template.
"subnet-1" : {
"Type" : "AWS::EC2::Subnet",
"Properties" : {
"CidrBlock" : "172.16.1.0/24",
"VpcId" : { "Ref" : "VPC" }
}
},
"subnet-2" : {
"Type" : "AWS::EC2::Subnet",
"Properties" : {
"CidrBlock" : "172.16.2.0/24",
"VpcId" : { "Ref" : "VPC" }
}
},
"DBSubnetGroup" : {
"Type" : "AWS::RDS::DBSubnetGroup",
"Properties" : {
"SubnetIds" : [
{ "Ref" : "subnet-1" },
{ "Ref" : "subnet-2" }
],
}
},
and in last you have to include DBSubnetGroup in your "DB" Object.
"DBSubnetGroupName": { "Ref": "DBSubnetGroup" }
You need to include the DBSubnetGroupName:
A DB subnet group to associate with the DB instance.
If there is no DB subnet group, then it is a non-VPC DB instance.
Create a DBSubnetGroup resource using subnets in your VPC, then tie that to your DBInstance:
"DBSubnetGroupName": { "Ref": "MySubnetGroup" }

AWS CloudFormation identify IP of another instance - circular dependency

I'm provisioning two instances with CloudFormation template. "MASTER" and "SLAVE".
At the userdata script I need to pass slave's private IP to master and master's IP to slave.
Here is my template:
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Description" : "",
"Parameters" : {
},
"Resources" : {
"InstanceSecurityGroup" : {
"Type" : "AWS::EC2::SecurityGroup",
"Properties" : {
"GroupDescription" : "Default Ports",
"SecurityGroupIngress" : [
{ "IpProtocol" : "tcp", "FromPort" : "22", "ToPort" : "22", "CidrIp" : "0.0.0.0/0"}
]
}
},
"MASTER" : {
"Type" : "AWS::EC2::Instance",
"Properties" : {
"Tags":[{"Key":"Name", "Value":"MASTER"}],
"SecurityGroups" : [ { "Ref" : "InstanceSecurityGroup" } ],
"KeyName" : "mykey",
"ImageId" : "ami-a25415cb",
"InstanceType": "m1.large",
"UserData" : { "Fn::Base64" : { "Fn::Join" : ["", [
"#!/bin/bash -ex", "\n",
"wget https://s3.amazonaws.com/mybucket/bootstrap.sh","\n",
"ROLE=MASTER SLAVEIP=",?????," sh bootstrap.sh","\n"
] ] } }
}
},
"SLAVE" : {
"Type" : "AWS::EC2::Instance",
"Properties" : {
"Tags":[{"Key":"Name", "Value":"SLAVE"}],
"SecurityGroups" : [ { "Ref" : "InstanceSecurityGroup" } ],
"KeyName" : "mykey",
"ImageId" : "ami-a25415cb",
"InstanceType": "m1.large",
"UserData" : { "Fn::Base64" : { "Fn::Join" : ["", [
"#!/bin/bash -ex", "\n",
"wget https://s3.amazonaws.com/mybucket/bootstrap.sh","\n",
"ROLE=SLAVE MASTERIP=",?????," sh bootstrap.sh","\n"
] ] } }
}
},
},
"Outputs" : {
}
}
What would be right replacement for ????? if it's possible at all and if not - what alternative can I use?
UPD: found this: {"Fn::GetAtt": ["MASTER","PrivateIp"]}, it works fine on it's own, but fails with "Template validation error: Circular dependency between resources: [SLAVE, MASTER]" if I'm trying to do both master and slave IPs.
If you're using VPCs and Subnets, you can do this by creating an AWS::EC2::NetworkInterface for each instance. Then use { "Fn::GetAtt": [ "MyNetworkInterface", "PrimaryPrivateIpAddress" ] } in user data to reference the internal IP address of the network interface
You associate a network interface with an EC2 instance using the NetworkInterfaces property
...
"MasterNetInt" : {
"Type" : "AWS::EC2::NetworkInterface",
"Properties" : {
"SubnetId": { "Ref" : "MySubnet" }
}
},
"SlaveNetInt" : {
"Type" : "AWS::EC2::NetworkInterface",
"Properties" : {
"SubnetId": { "Ref" : "MySubnet" }
}
},
"Master" : {
"Type" : "AWS::EC2::Instance",
"Properties" : {
"SecurityGroups" : [ { "Ref" : "InstanceSecurityGroup" } ],
"KeyName" : "mykey",
"ImageId" : "ami-a25415cb",
"InstanceType": "m1.large",
"SubnetId": { "Ref" : "MySubnet" },
"NetworkInterfaces": [ { "Ref" : "MasterNetInt" } ],
"UserData": { "Fn::Base64" : { "Fn::Join" : ["", [
"#!/bin/bash -ex", "\n",
"wget https://s3.amazonaws.com/mybucket/bootstrap.sh","\n",
"ROLE=MASTER SLAVEIP=", { "Fn::GetAtt": [ "SlaveNetInt", "PrimaryPrivateIpAddress" ] }," sh bootstrap.sh","\n"
] ] } }
}
},
"Slave" : {
"Type" : "AWS::EC2::Instance",
"Properties" : {
"SecurityGroups" : [ { "Ref" : "InstanceSecurityGroup" } ],
"KeyName" : "mykey",
"ImageId" : "ami-a25415cb",
"InstanceType": "m1.large",
"SubnetId": { "Ref" : "MySubnet" },
"NetworkInterfaces": [ { "Ref" : "SlaveNetInt" } ],
"UserData" : { "Fn::Base64" : { "Fn::Join" : ["", [
"#!/bin/bash -ex", "\n",
"wget https://s3.amazonaws.com/mybucket/bootstrap.sh","\n",
"ROLE=SLAVE MASTERIP=", { "Fn::GetAtt": [ "MasterNetInt", "PrimaryPrivateIpAddress" ] }," sh bootstrap.sh","\n"
] ] } }
}
}
...
If you're unfamiliar with setting up a VPC and Subnets, read the following documentation: http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Scenario1.html
and reference these template examples: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/sample-templates-services-us-west-2.html#d0e113371
The basic requirements are:
AWS::EC2::VPC
AWS::EC2::InternetGateway
AWS::EC2::VPCGatewayAttachment
AWS::EC2::RouteTable
AWS::EC2::Route
AWS::EC2::Subnet
AWS::EC2::SubnetRouteTableAssociation
AWS::EC2::NetworkAcl
AWS::EC2::SubnetNetworkAclAssociation
AWS::EC2::NetworkAclEntry
AWS::EC2::NetworkInterface
AWS::EC2::Instance

Reusing AWS::CloudFormation::Init (and userdata?) for multiple instances

Is it possible to reuse the same bootstrapping config from AWS::CloudFormation::Init (and/or userdata) for multiple EC2::Instances in a template?
I need to set the content of 3 files and then run 3 commands to bootstrap all servers, but the Metadata block is about 30 lines long (and is likely to grow).
Each server instance has a different set of Tags, some have more tags than others.
Ideally, I think that you should be able to declare the AWS::CloudFormation::Init as a resource, and refer to it from multiple EC2::Instances, but I don't think that this is possible.
I initially thought (as a newbie) that AWS::CloudFormation::CustomResource might be appropriate, but I don't think that it is.
I'm currently thinking of using a AWS::CloudFormation::Stack to import a shared instance template, but I would need to somehow pass the Tags parameter for each resource in the stack template into the instance template. The question is - if this is the best approach, what do I enter in the 3 locations that currently have ?????
(Bonus credit - what's the difference between userdata and this init block?)
stack.template
...
"Resources" : {
"Server1" : {
"Type": "AWS::CloudFormation::Stack",
"Properties": {
"Parameters": {
"InstanceType": "m1.medium",
...
"Tags": { ???? }
},
"TemplateURL": "https://s3.amazonaws.com/mybucket/instance.template"
}
instance.template
...
"Parameters" : {
"InstanceType" : {...}
"KeyName": {...}
...
"Tags": {
????
}
},
"Resources" : {
"Instance" : {
"Type" : "AWS::EC2::Instance",
"Metadata" : {
"AWS::CloudFormation::Init" : {
"config" : {
"files" : {
"/etc/apt/apt.conf.d/99auth" : {
"content" : "APT::Get::AllowUnauthenticated yes;"
},
"/etc/apt/sources.list.d/my-repo.list" : {
"content" : "deb http://my-repo/repo apt/"
},
},
"commands" : {
"01-apt-get update" : {
"command" : "apt-get update"
},
"02-apt-get install puppet" : {
"command" : "apt-get install puppet my-puppet-config"
},
"03-puppet apply" : {
"command" : "puppet apply"
}
}
}
}
},
"Properties" : {
"InstanceType" : {"Ref" : "InstanceType"},
"ImageId" : "ami-84a333be",
"KeyName" : {"Ref" : "KeyName"},
"SubnetId" : {"Ref" : "SubnetId"},
"SecurityGroupIds" : [ { "Ref" : "SecurityGroupId"] } ],
"Tags" : [
????
]
}
}
}
Is it possible to reuse the same bootstrapping config from
AWS::CloudFormation::Init (and/or userdata) for multiple
EC2::Instances in a template?
No, this is not possible with the AWS::EC2::Instance resource in a single template. However, there is a resource type AWS::AutoScaling::LaunchConfiguration, but today this resource is only applicable to Auto Scaling groups. Ideally, AWS would provide a similar resource type that can be applied to multiple AWS::EC2::Instance resources. That being said, there is a lot of value in using Auto Scaling groups.
Here is a simple example that would allow you to do this with a single Launch Configuration and multiple Auto Scaling group resources in a single template. Auto scaling is typically configured with more than one instance, but I am using a MinSize and MaxSize of 1 to mirror the configuration you were going after with the AWS::EC2::Instance resource type. Even though we are using a single instance with an Auto Scaling group, we still gain the benefits of Auto Scaling with single instance resiliency. If the instance becomes unhealthy, Auto Scaling will automatically replace the instance.
{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources" : {
"LaunchConfig" : {
"Type" : "AWS::AutoScaling::LaunchConfiguration",
"Properties" : {
"InstanceType" : { "Ref" : "InstanceType" },
"ImageId" : "ami-84a333be",
"KeyName" : { "Ref" : "KeyName" },
"SecurityGroupIds" : [{"Ref" : "SecurityGroupId"}],
"UserData" : { "Fn::Base64" : { "Fn::Join" : [ "", [
"#!/bin/bash -v\n",
"# Run cfn-init\n",
"/opt/aws/bin/cfn-init -v ",
" -stack ", { "Ref": "AWS::StackName" },
" -resource LaunchConfig ",
" --region ", { "Ref" : "AWS::Region" }, "\n",
"# Signal success\n",
"/opt/aws/bin/cfn-signal -e $? '", { "Ref" : "WaitConditionHandle" }, "'\n"
]]}}
},
"Metadata" : {
"AWS::CloudFormation::Init" : {
"config" : {
"files" : {
"/etc/apt/apt.conf.d/99auth" : {
"content" : "APT::Get::AllowUnauthenticated yes;"
},
"/etc/apt/sources.list.d/my-repo.list" : {
"content" : "deb http://my-repo/repo apt/"
}
},
"commands" : {
"01-apt-get update" : {
"command" : "apt-get update"
},
"02-apt-get install puppet" : {
"command" : "apt-get install puppet my-puppet-config"
},
"03-puppet apply" : {
"command" : "puppet apply"
}
}
}
}
}
},
"ASG1" : {
"Type" : "AWS::AutoScaling::AutoScalingGroup",
"Properties" : {
"AvailabilityZones" : [ { "Ref" : "AZ" } ],
"VPCZoneIdentifier" : [ { "Ref" : "SubnetId" } ],
"LaunchConfigurationName" : { "Ref" : "LaunchConfig" },
"MaxSize" : "1",
"MinSize" : "1",
"Tags" : [
{ "Key" : "Name", "Value": "Server1", "PropagateAtLaunch" : "true" },
{ "Key" : "Version", "Value": "1.0", "PropagateAtLaunch" : "true" }
]
}
},
"ASG2" : {
"Type" : "AWS::AutoScaling::AutoScalingGroup",
"Properties" : {
"AvailabilityZones" : [ { "Ref" : "AZ" } ],
"VPCZoneIdentifier" : [ { "Ref" : "SubnetId" } ],
"LaunchConfigurationName" : { "Ref" : "LaunchConfig" },
"MaxSize" : "1",
"MinSize" : "1",
"Tags" : [
{ "Key" : "Name", "Value": "Server2", "PropagateAtLaunch" : "true" },
{ "Key" : "Version", "Value": "1.0", "PropagateAtLaunch" : "true" }
]
}
},
"WaitConditionHandle" : {
"Type" : "AWS::CloudFormation::WaitConditionHandle"
},
"WaitCondition" : {
"Type" : "AWS::CloudFormation::WaitCondition",
"Properties" : {
"Handle" : { "Ref" : "WaitConditionHandle" },
"Timeout" : "300"
}
}
}
}
This is an incomplete example and there is much more you can do with Auto Scaling, but I just wanted to give you a simple example sharing a Launch Configuration with multiple instances. Each Auto Scaling group does define its own set of tags.
I'm currently thinking of using a AWS::CloudFormation::Stack to import
a shared instance template, but I would need to somehow pass the Tags
parameter for each resource in the stack template into the instance
template. The question is - if this is the best approach, what do I
enter in the 3 locations that currently have ?????
I would recommend the solution I described above, but I would also like to answer your questions on how use tags with the nested stack approach.
stack.template
{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources" : {
"Server1" : {
"Type": "AWS::CloudFormation::Stack",
"Properties": {
"TemplateURL": "https://s3.amazonaws.com/mybucket/instance.template",
"Parameters": {
"InstanceType": "m1.medium",
"TagName": "Server1"
"TagVersion": "1.0"
}
}
},
"Server2" : {
"Type": "AWS::CloudFormation::Stack",
"Properties": {
"TemplateURL": "https://s3.amazonaws.com/mybucket/instance.template",
"Parameters": {
"InstanceType": "m1.medium",
"TagName": "Server2"
"TagVersion": "1.0"
}
}
}
}
}
instance.template
{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters" : {
"InstanceType" : {...},
"KeyName": {...}
"TagName": {
"Description" : "The name tag to be applied to each instance",
"Type" : "String"
},
"TagVersion": {
"Description" : "The version tag to be applied to each instance",
"Type" : "String"
}
},
"Resources" : {
"Instance" : {
"Type" : "AWS::EC2::Instance",
"Metadata" : {
"AWS::CloudFormation::Init" : {
...
}
},
"Properties" : {
"InstanceType" : { "Ref" : "InstanceType" },
"ImageId" : "ami-84a333be",
"KeyName" : { "Ref" : "KeyName" },
"SubnetId" : { "Ref" : "SubnetId" },
"SecurityGroupIds" : [ { "Ref" : "SecurityGroupId"] } ],
"Tags" : [
{ "Key" : "Name", "Value": { "Ref" : "TagName" } },
{ "Key" : "Version", "Value": { "Ref" : "TagVersion" } },
]
}
}
}
}
There is not a standard for passing an entire array of tags as a parameter, so you can see I simply broke each tag out into its own parameter and passed these on to the nested stacks.
(Bonus credit - what's the difference between userdata and this init
block?)
UserData allows you to pass arbitrary data to the instance at first boot. Often this is a shell script that can automate tasks when the instance starts. For example, you could simply run a yum update.
"UserData" : { "Fn::Base64" : { "Fn::Join" : [ "", [
"#!/bin/bash\n"
"yum update -y", "\n"
]]}}
UserData becomes even more useful when combined with the AWS::CloudFormation::Init metadata which allows you to structure your bootstrapping configuration. In this case, UserData is simply used to invoke the cfn-init script that executes the AWS::CloudFormation::Init metadata. I've included this pattern in my first example above using a Launch Configuration. It is important to note that the UserData section is executed only once during the first boot of the instance. This is important to keep in mind when thinking about how you want to handle updates to your instances.