I'm designing an AWS stack that contains multiple instances that run a handful of services comprised of a few tasks each. One of these services uses NFS to store configuration, and this configuration needs to be setup ONCE when the stack is created.
I've come up with a way to run a configuration script ONCE when the stack is created:
Configure the service that needs to configure itself to run a single task
When the task starts, check if the configuration exists. If it doesn't, run a configuration script and then update the desired task count so that other instances are created
(1) is necessary to avoid a race condition.
Although this works well, it strikes me as a very round-about way to achieve something simple: run a bash script ONCE when my stack is created. Is there a better way to do this?
You can run a one-off Bash script using an AWS::EC2::Instance resource with an InstanceInitiatedShutdownBehavior property of terminate (to terminate the instance after the script executes), and a DependsOn attribute set to the last-created resource in your stack (so the EC2 instance gets created and the Bash script gets executed at the end):
Description: Run a bash script once when a stack is created.
Mappings:
# amzn-ami-hvm-2016.09.1.20161221-x86_64-gp2
RegionMap:
us-east-1:
"64": "ami-9be6f38c"
Resources:
MyResource:
Type: AWS::SNS::Topic
WebServer:
Type: AWS::EC2::Instance
DependsOn: MyResource
Properties:
ImageId: !FindInMap [ RegionMap, !Ref "AWS::Region", 64]
InstanceType: m3.medium
InstanceInitiatedShutdownBehavior: terminate
UserData:
"Fn::Base64":
!Sub |
#!/bin/bash
# [Contents of bash script here...]
shutdown -h now
Related
I have a working cloudFormation yaml template. I know its working because when I go to the aws portal and create a stack by clicking-away the stack gets created.
However, when I try to use the cloudformation on the command line the same yaml errors out.
I'm at loss to what's causing this issue. Does anyone know what may be causing the failure?
Here is the command I am calling
aws cloudformation create-stack --stack-name ${stack_name} --template-body file://template.yaml --region ${region}
where region is the same region I am in the aws portal. And here is the template.yaml
---
AWSTemplateFormatVersion: 2010-09-09
Description: EC2 example instance
Resources:
TestEC2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-01ec0fa63b2042232
InstanceType: t3.medium
SubnetId: subnet-*********
UserData:
Fn::Base64:
!Sub |
#!/bin/bash -xe
echo "Running apt install -y git nfs-common binutils jq"
apt-get install -y git nfs-common binutils jq
when I run the command I see the stack starting to be created on the portal with the following events
ec2-boilerplate ROLLBACK_COMPLETE -
TestEC2Instance DELETE_COMPLETE -
TestEC2Instance DELETE_IN_PROGRESS -
ec2-boilerplate ROLLBACK_IN_PROGRESS The following resource(s) failed to create: [TestEC2Instance]. Rollback requested by user.
TestEC2Instance CREATE_FAILED Instance i-0bdd3e7ee34edf1ef failed to stabilize. Current state: shutting-down. Reason: Client.InternalError: Client error on launch
TestEC2Instance CREATE_IN_PROGRESS Resource creation Initiated
TestEC2Instance CREATE_IN_PROGRESS -
ec2-boilerplate CREATE_IN_PROGRESS User Initiated
Is it something about my template.yaml? about my command line call? some environment variable?
This may be related to the length of time the instance is taking to create, but your 'userdata' script seems simple enough that it is hard to imagine that being the case. The AWS documentation states that this error relates to the resource creation time exceeding the CloudFormation timeout:
A resource didn't respond because the operation exceeded the AWS
CloudFormation timeout period or an AWS service was interrupted. For
service interruptions, check that the relevant AWS service is running,
and then retry the stack operation.
One quick thing I would check is that the -e option to bash in your userdata script isn't causing it to fail in some way that hinders instance creation.
Another possible solution, per the AWS recommendation, is to ensure that your CloudFormation stack is provided a service role when it is created. This will allow it to create infrastructure beyond the timeout imposed when using the creating user's IAM permissions.
im working on a template in which i deploy an ec2 instance, in the instances user data, the instance pulls a script from a git repo and uses that script create an AMI. I would like to refer to that newly created ami’s ID in another resource in the same cloudformation stack using the either by using !ref or some other way.
so far I have placed this line below in the user data to get the name of the ami
export AMIID=$(aws ec2 describe-images --filters "Name=name,Values=ami-name" | jq -r ".Images[].ImageId")
and this line to create an entry to put the AMI ID in the parameter store
aws ssm put-parameter --name aminame --type String --value "$AMIID"
In the cloudformation stack I have a parameter here
AMI:
Type : 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
Default: aminame
and in the resource block I have this the reference to the ami that looks something like this
EC2Instance:
Type: "AWS::EC2::Instance"
CreationPolicy:
ResourceSignal:
Timeout: PT120M
Properties:
ImageId: !Ref AMI
UserData:
Fn::Base64: |
#!/bin/bash
So far when I run this I get an error stating that the parameter cannot be found.. which makes sense, however is there any other way to do something like this ?
Technically you can do that using lambda:
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-custom-resources.html
I'm trying to create a CF script that creates an EC2 and then automatically mounts an EFS. Here's the relevant bit of the template. I find that the packages are not loaded: amazon-efs-utils, nfs-utils.
Therefore if the mount command is executed it will fail.
I've verified that my other stack has what I need and the output variable is correct: !ImportValue dmp356-efs-EFSId
If I log into my new instance and do the steps manually it works fine and I can see my files in the EFS. Naturally I suspect that my CF script is wrong in some way, although it validates if I use "aws cloudformation validate-template ..." and it deploys with a successful conclusion. As I said, I can log into the new instance, it doesn't rollback.
Resources:
TestHost:
Type: AWS::EC2::Instance
Metadata:
AWS::CloudFormation::Init:
configSets:
MountConfig:
- setup
- mount
setup:
packages:
yum:
amazon-efs-utils: []
nfs-utils: []
commands:
01_createdir:
command:
"mkdir /nfs"
mount:
commands:
01_mount:
command: !Sub
- mount -t efs ${EFS_ID} /nfs
- { EFS_ID: !ImportValue dmp356-efs-EFSId }
02_mount:
command:
"chown ec2-user.ec2-user /nfs"
I have a cloudfromation script that creates some ec2 instances, and later attaches them to an ELB.
I have a python server script written that I would like to have on the ec2 as soon as they are created.
Right now what I do is after the cloudformation script finishes, I use SCP to pass the script to the ec2 instances.
I was wondering if there was a way to do this within the cloudfromation, mabe under UserData?
I should point out I am very new to cloud formation. I have gone over the documentation, but have not been able to do this yet.
[EDIT] I think its important to state that I have a deploy.sh script that I run to create the stack. the script sits in the dame dir as my python server script. I AM NOT USING THE AWS CONSOLE.
this is my instance code in the cloudformation script:
EC2Instance2:
Type: AWS::EC2::Instance
Properties:
InstanceType: !Ref InstanceType
SecurityGroupIds:
- !Ref InstanceSecurityGroup
KeyName: !Ref KeyName
ImageId: !Ref LatestAmiId
UserData:
Fn::Base64:
!Sub |
#!/bin/bash
sleep 20
sudo apt-get update
sudo apt-get install python3-pip -y
sudo apt-get install python3-flask -y
I was wondering if there was a way to do this within the cloudfromation, mabe under UserData?
Yes, UserData would be the way to do it. For that you could store your file in S3. For that to work you would need to add instance role to your instance with S3 permissions. The you would use AWS CLI to copy your file from S3 to the instance.
I'm creating a cloud9 instance in cloudformation as follows:
Resources:
DevEnv:
Type: 'AWS::Cloud9::EnvironmentEC2'
Properties:
SubnetId: !Ref PublicSubnet
Name: MyEnvironment
InstanceType: !Ref Cloud9InstanceType
Subsequently, I want to run a bash script on that EC2 instance (within the cloudformation script) to set it up properly. How do I do that?
Unfortunately you can't. AWS::Cloud9::EnvironmentEC2 doesn't expose UserData or something like that and you cannot run SSM Documents against Cloud9 instances.
The closest you can get is using Repositories to clone a AWS CodeCommit into the instance, and that repository has a script that you run manually first time you connect...