Reusing AWS :: CloudFormation :: Init (and userdata?) For Multiple Instances

Is it possible to reuse the same bootstrap configuration from AWS::CloudFormation::Init (and / or userdata ) for multiple EC2::Instance in a template?

I need to set the contents of 3 files, and then run 3 commands to load all the servers, but the Metadata block has a length of about 30 lines (and will probably grow). Each server instance has a different set of tags, some of which have more tags than others.

Ideally, I think that you should be able to declare AWS::CloudFormation::Init as a resource and reference it from several EC2::Instance s, but I do not think it is possible.

I initially thought (as a newbie) that AWS::CloudFormation::CustomResource might be appropriate, but I don't think so.

I'm currently thinking of using AWS::CloudFormation::Stack to import a shared instance template, but I need to somehow pass the Tags parameter for each resource in the stack template to the instance template. Question: if this is the best approach, what should 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" : [ ???? ] } } } 
+5
source share
1 answer

Can I reuse the same bootstrap configuration from AWS :: CloudFormation :: Init (and / or user data) for multiple EC2 :: Instances in the template?

No, this is not possible with the AWS :: EC2 :: Instance resource in the same template. However, there is an AWS :: AutoScaling :: LaunchConfiguration resource type, but today this resource is applicable only to auto-scaling groups. Ideally, AWS will provide a similar resource type that can be applied to multiple AWS :: EC2 :: Instance resources. This means that when using autoscale groups, there is a value .

Here is a simple example that will allow you to do this with a single start configuration and several autoscale groups in a single template. Auto-scaling is usually configured with multiple instances, but I use MinSize and MaxSize 1 to mirror the configuration you came up with with the resource type AWS :: EC2 :: Instance. Although we use the same instance with the Auto Scaling group, we still get the benefits of Auto Scaling with the fault tolerance of one instance. If an 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 not a complete example, and much more can be done with Auto Scaling, but I just wanted to give you a simple example of sharing a launch configuration with multiple instances. Each Auto Scaling group defines its own set of tags.


I'm currently thinking of using AWS :: CloudFormation :: Stack to import a shared instance template, but I need to somehow pass the parameter tags for each resource in the stack template to the instance template. The question is - if this is the best approach, what should I do? Enter in the 3 locations that currently have

I would recommend the solution described above, but would also like to answer your questions on how to use tags with a nested stack.

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 no standard for passing the entire array of tags as a parameter, so you can see that I just broke each tag into its own parameter and passed them to nested stacks.


(Bonus credit - what's the difference between userdata and this init block?)

UserData allows you to pass arbitrary data to the instance on first boot. This is often a shell script that can automate tasks when the instance starts. For example, you can simply run the yum update.

 "UserData" : { "Fn::Base64" : { "Fn::Join" : [ "", [ "#!/bin/bash\n" "yum update -y", "\n" ]]}} 

UserData becomes even more useful when combined with AWS :: CloudFormation :: Init metadata, which allows you to structure your bootstrap configuration. In this case, UserData is simply used to invoke the cfn-init script that runs the AWS :: CloudFormation :: Init metadata. I included this template in my first example above using the launch configuration. It is important to note that the UserData section is executed only once during the first load of the instance. This is important to keep in mind when you think about how you want to handle updates for your instances.

+8
source

Source: https://habr.com/ru/post/1209201/


All Articles