AWS Cloudformation : Order of Execution among ConfigSets, UserData, Cloud-Init - amazon-web-services

I have created a template where I created EC2 instance and I used cfn-init to process the configsets, and in the Userdata section of the instance, I wrote some commands to be executed by cloud-init and some commands to be executed without cloud-init.
I am not sure which commands runs in which sequence?
What I mean is, in which order the commands are executed? For example:
Commands in the configsets
Commands in the cloud-init section of the userdata
Commands in the Userdata
Part of my code is below:
UserData:
Fn::If:
- DatadogAgentEnabled
-
Fn::Base64: !Sub |
#!/bin/bash -xe
yum update -y
yum update -y aws-cfn-bootstrap
/opt/aws/bin/cfn-init --stack ${AWS::StackName} --resource GhostServer --configsets prepare_instance_with_datadog --region ${AWS::Region}
/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource GhostServer --region ${AWS::Region}
#cloud-config <----cloud-init section inside the Userdata
runcmd:
- [ sh, -c, "sed 's/api_key:.*/api_key: {DatadogAPIKey}/' /etc/datadog-agent/datadog.yaml.example > /etc/datadog-agent/datadog.yaml" ]
- systemctl restart datadog-agent

From the AWS documentation
UserData :
The user data to make available to the instance. For more
information, see Running commands on your Linux instance at launch
(Linux) and Adding User Data (Windows). If you are using a command
line tool, base64-encoding is performed for you, and you can load the
text from a file. Otherwise, you must provide base64-encoded text.
User data is limited to 16 KB.
So Basically we define UserData to execute some commands at the launch of our EC2 instance.
Reference : https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-instance.html
To Answer your question first of all UserData commands will be executed and all the commands specified in this section will execute sequentially so in your example you invoked cfn helper scripts first and then cloud init thus configsets will be applied first and then cloud init commands will be called.
Inside that UserData section we are invoking cfn helper scripts which reads cloudformation template metadata and executes all the configsets defined under AWS::CloudFormation::Init:
From the AWS Documentation :
The cfn-init helper script reads template metadata from the
AWS::CloudFormation::Init key and acts accordingly to:
Fetch and parse metadata from AWS CloudFormation
Install packages
Write files to disk
Enable/disable and start/stop services
Reference : https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-init.html

Related

How to run ec2 UserData script on cloudformation update

I have ec2 userData script that doing docker-compose pull and up.
I want to run "aws cloudformation update-stack" and load new docker images.
Each time ${imageTag} property changes.
This is my cloudformation instance yml:
myInstance:
Type: 'AWS::EC2::Instance'
Metadata:
'AWS::CloudFormation::Init':
configSets:
configs:
- "configDockers"
- "configOther"
configDocker:
commands:
a:
command: 'echo -e IMAGE_TAG=${imageTag} >> .env'
b:
command: 'docker-compose pull'
c:
command: 'docker-compose up'
Properties:
UserData:
Fn::Base64:
!Sub |
runcmd:
- /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource myInstance --region ${AWS::Region} -c configs
- /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource myInstance --region ${AWS::Region}
I tried to add "docker-compose down", remove old images in the UserData script, and add cloud_final_modules to run UserData each startup:
myInstance:
Type: 'AWS::EC2::Instance'
Metadata:
'AWS::CloudFormation::Init':
configSets:
configs:
- "configDockers"
- "configOther"
configDocker:
commands:
a:
command: 'echo -e IMAGE_TAG=${imageTag} >> .env'
b:
command: 'docker-compose down'
c:
command: 'docker images -q | xargs -r sudo docker rmi'
d:
command: 'docker-compose pull'
e:
command: 'docker-compose up'
Properties:
UserData:
Fn::Base64:
!Sub |
#cloud-config
cloud_final_modules:
- [scripts-user, always]
runcmd:
- /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource myInstance --region ${AWS::Region} -c configs
- /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource myInstance --region ${AWS::Region}
But after "aws cloudformation update-stack" the script does not run again dispite the imageTag changed. it runs only if I change some value under runcmd.
How can I run the UserData script each time "aws cloudformation update-stack" running (each time imageTag changes)?
How can I run some UserData commands only on first startup, other commands on each reboot and other commands on stop instance?
for example: I want to run "docker-compose down" only on instance stop but other commands command: 'docker-compose pull/up' on each instance reboot or "aws cloudformation update-stack" and some initial commands only on first setup.
How can I run some UserData commands only on first startup, other commands on each reboot and other commands on stop instance?
You can't. UserData is meant to run only at instance launch, not reboots, starts or stops (one exception mentioned below). In order to do so, you have to implement all this functionality yourself.
This is commonly done though definition of custom systemd unit files. Thus you would have to create such unit files for your docker-compose.
The only exception is, a rather hackish way, of running userdata at instance start described in recent AWS blog post:
How can I utilize user data to automatically execute a script with every restart of my Amazon EC2 instance?

Cloudformation Stack not receiving signal from userdata in EC2 Launch Template

In my template, I'm provisioning an ASG that uses an EC2 Launch template
In the UserData section, I do a cfn-init to provision the instance, which works fine.
However when I do the cfn-signal command, the command is successful (exit code of 0), but the Cloudformation stack never receives it, and the stack creation/update fails with Failed to receive 1 resource signal(s) for the current batch. Each resource signal timeout is counted as a FAILURE.
When I check Cloudtrail, I see that the SignalResource API call was completed, signaling the correct Stack and resource with a SUCCESS, (but a responseElements of null)
Excerpt from my Cloudformation template:
Resources:
MyLaunchTemplate:
Type: AWS::EC2::LaunchTemplate
Metadata:
'AWS::CloudFormation::Init':
configSets:
# lots-o-stuff to be done by cfn-init
Properties:
LaunchTemplateData:
# Remove other attributes for brevity
UserData:
Fn::Base64:
!Sub |
#!/bin/bash -x
yum update -y
# gather ENI
/opt/aws/bin/cfn-init -c install \
--stack ${AWS::StackName} \
--resource MyLaunchTemplate \
--region ${AWS::Region}
/opt/aws/bin/cfn-signal -e $? \
--stack ${AWS::StackName} \
--resource MyAsg \
--region ${AWS::Region}
echo $?
LaunchTemplateName: my-launch-template
MyAsg:
Type: AWS::AutoScaling::AutoScalingGroup
CreationPolicy:
AutoScalingCreationPolicy:
MinSuccessfulInstancesPercent: 100
ResourceSignal:
Count: 1
Timeout: PT10M
UpdatePolicy:
AutoScalingReplacingUpdate:
WillReplace: true
Properties:
# Remove other attributes for brevity
LaunchTemplate:
LaunchTemplateId: !Ref MyLaunchTemplate
Version: !GetAtt MyLaunchTemplate.LatestVersionNumber
Any idea what I'm missing here?
It appears that the Amazon Linux AMI version is not the latest and cfn-signal script isn't installed on the instance. Bootstrap the EC2 instance using aws-cfn-bootstrap package.
Add yum install -y aws-cfn-bootstrap in UserData section.
Another possible cause could be that the value of the Timeout property for the CreationPolicy attribute is too low. Make sure that the value is high enough for tasks to run before the cfn-signal script sends signals to AWS CloudFormation resources.
If it still doesn't work, then do check out AWS Troubleshooting Guide for this issue.
I had also faced a similar issue recently. It seems like cfn-signals don’t work with launch template for some reason. For test purposes I changed my launch template to a launch configuration and the cfn signal worked totally fine. It’s weird why AWS don’t support launch templates.

AWS::CloudFormation::Init are commands stored on ec2 instance?

I'm using AWS::CloudFormation::Init and have a lengthy list of commands.
In my commands I'm using many intrinsic functions to drop in variables. For example:
commands:
'01':
command: !Sub 'echo ${AWS::StackName} > ~/cfn-stack.txt'
Once my stack is deployed, and the EC2 instance is created, is there a place on the instance where I can view the raw commands that were run with the resolved variables dropped in?

AWS Cloud Formation cfn-signal: command not found

I'm setting up an auto-scaling group using cloud formation, and running a script via UserData on server startup. At the end of the user data script, I'm attempting to call cfn-signal to let the autoscaling group know that instance startup is complete:
cfn-signal -s true --stack stack_name --resource resource_name --region region_name
However, I'm receiving the following error:
cfn-signal: command not found
cfn-signal is definitely installed on the machine:
[ec2-user#ip-xxx-xx-xx-xx ~]$ which cfn-signal
/opt/aws/bin/cfn-signal
It turns out that the symlink to cfn-signal must not be created until after the UserData script is run.
I was able to use cfn-signal by calling it with the full path /opt/aws/bin/cfn-signal

Where is CloudFormation template located on the instance, which is called by cloud-init?

I understand the AWS::CloudFormation::Init block of the template is executed by cfn-init script.
Here is the syntax I use to do so:
/usr/local/bin/cfn-init -v --stack WebS<erver1 --resource WebServerInstance --region eu-central-1 --configset InstallAndRun && cat /var/log/cfn-init.log
I can do the call via UserData or via SSH.
Now I would like to have the possibility to modify the AWS::CloudFormation::Init part of the template and repeat the cnf-init call via ssh.
Question:
Where is the file with the cloud formation code located on my instance, which is executed by cfn-init?
The cfn-init command uses a configuration file located at /var/lib/cfn-init/data/metadata.json. The JSON file contains the config sets defined in the AWS::CloudFormation::Init block of a Cloud Formation template.
I've confirmed the path above on Amazon Linux 2. The location on other operating systems may be different.
All of the cloud init files are in /var/lib/cloud (on Centos 7) and the userdata script is in /var/lib/cloud/instance
See my other answer
cloud-init: What is the execution order of cloud-config directives?
for general hints on where the source to cloud-init is, execution ordering