{"id":4217,"date":"2021-03-28T17:39:41","date_gmt":"2021-03-28T15:39:41","guid":{"rendered":"http:\/\/miro.borodziuk.eu\/?p=4217"},"modified":"2021-06-28T16:44:44","modified_gmt":"2021-06-28T14:44:44","slug":"cloudformation-cfn","status":"publish","type":"post","link":"http:\/\/miro.borodziuk.eu\/index.php\/2021\/03\/28\/cloudformation-cfn\/","title":{"rendered":"CloudFormation &#8211; 3 &#8211; User data, cfn"},"content":{"rendered":"<p><!--more--><\/p>\n<p><span style=\"color: #3366ff;\">User Data in EC2 for CloudFormation<\/span><\/p>\n<ul>\n<li>We can have user data at EC2 instance launch through the console<\/li>\n<li>We can also include it in CloudFormation<\/li>\n<li>The important thing to pass is the entire script through the function <code>Fn::Base64<\/code><\/li>\n<li>Good to know: user data script log is in <code>\/var\/log\/cloud-init-output.log <\/code><\/li>\n<\/ul>\n<p>Let&#8217;s see how to do this in CloudFormation. We have such an yml file:<\/p>\n<pre class=\"lang:default decode:true\">---\r\nParameters:\r\n  SSHKey:\r\n    Type: AWS::EC2::KeyPair::KeyName\r\n    Description: Name of an existing EC2 KeyPair to enable SSH access to the instance\r\n\r\nResources:\r\n  MyInstance:\r\n    Type: AWS::EC2::Instance\r\n    Properties:\r\n      AvailabilityZone: eu-central-1a\r\n      ImageId: ami-00a205cb8e06c3c4e\r\n      InstanceType: t2.micro\r\n      KeyName: !Ref SSHKey\r\n      SecurityGroups:\r\n        - !Ref SSHSecurityGroup\r\n      # we install our web server with user data\r\n      UserData: \r\n        Fn::Base64: |\r\n          #!\/bin\/bash -xe\r\n          yum update -y\r\n          yum install -y httpd\r\n          systemctl start httpd\r\n          systemctl enable httpd\r\n          echo \"Hello World from user data\" &gt; \/var\/www\/html\/index.html\r\n\r\n  # our EC2 security group\r\n  SSHSecurityGroup:\r\n    Type: AWS::EC2::SecurityGroup\r\n    Properties:\r\n      GroupDescription: SSH and HTTP\r\n      SecurityGroupIngress:\r\n      - CidrIp: 0.0.0.0\/0\r\n        FromPort: 22\r\n        IpProtocol: tcp\r\n        ToPort: 22\r\n      - CidrIp: 0.0.0.0\/0\r\n        FromPort: 80\r\n        IpProtocol: tcp\r\n        ToPort: 80<\/pre>\n<p>&nbsp;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-4184\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation030.jpg\" alt=\"\" width=\"664\" height=\"698\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation030.jpg 664w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation030-285x300.jpg 285w\" sizes=\"(max-width: 664px) 100vw, 664px\" \/><\/p>\n<p><code>Next-&gt;<\/code><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-4183\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation029.jpg\" alt=\"\" width=\"693\" height=\"480\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation029.jpg 693w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation029-300x208.jpg 300w\" sizes=\"(max-width: 693px) 100vw, 693px\" \/><\/p>\n<p><code>Next -&gt; Create stack<\/code><\/p>\n<p>After the EC2 instance has been created we can check the public DNS address of the EC2 and go to that adress in the browser:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-4186\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation031.jpg\" alt=\"\" width=\"751\" height=\"141\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation031.jpg 751w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation031-300x56.jpg 300w\" sizes=\"(max-width: 706px) 89vw, (max-width: 767px) 82vw, 740px\" \/><\/p>\n<p>We can connect to the EC2 through SSH and check the output of user data:<\/p>\n<pre class=\"lang:default decode:true \">cat \/var\/log\/cloud-init-output.log<\/pre>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #3366ff;\">cfn init<\/span><\/p>\n<ul>\n<li><code>AWS::CloudFormation::Init<\/code> must be in the Metadata of a resource<\/li>\n<li>With the cfn-init script, it helps make complex EC2 configurations readable<\/li>\n<li>The EC2 instance will query the CloudFormation service to get init data<\/li>\n<li>Logs go to<code> \/var\/log\/cfn-init.log<\/code><\/li>\n<\/ul>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4190 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation033.jpg\" alt=\"\" width=\"744\" height=\"749\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation033.jpg 744w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation033-298x300.jpg 298w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation033-150x150.jpg 150w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation033-100x100.jpg 100w\" sizes=\"(max-width: 706px) 89vw, (max-width: 767px) 82vw, 740px\" \/><\/p>\n<p>Consider such a tamplate:<\/p>\n<pre class=\"lang:default decode:true \">---\r\nParameters:\r\n  SSHKey:\r\n    Type: AWS::EC2::KeyPair::KeyName\r\n    Description: Name of an existing EC2 KeyPair to enable SSH access to the instance\r\n\r\nResources:\r\n  MyInstance:\r\n    Type: AWS::EC2::Instance\r\n    Properties:\r\n      AvailabilityZone: eu-central-1a\r\n      ImageId: ami-00a205cb8e06c3c4e\r\n      InstanceType: t2.micro\r\n      KeyName: !Ref SSHKey\r\n      SecurityGroups:\r\n        - !Ref SSHSecurityGroup\r\n      # we install our web server with user data\r\n      UserData: \r\n        Fn::Base64:\r\n          !Sub |\r\n            #!\/bin\/bash -xe\r\n            # Get the latest CloudFormation package\r\n            yum update -y aws-cfn-bootstrap\r\n            # Start cfn-init\r\n            \/opt\/aws\/bin\/cfn-init -s ${AWS::StackId} -r MyInstance --region ${AWS::Region} || error_exit 'Failed to run cfn-init'\r\n    Metadata:\r\n      Comment: Install a simple Apache HTTP page\r\n      AWS::CloudFormation::Init:\r\n        config:\r\n          packages:\r\n            yum:\r\n              httpd: []\r\n          files:\r\n            \"\/var\/www\/html\/index.html\":\r\n              content: |\r\n                &lt;h1&gt;Hello World from EC2 instance!&lt;\/h1&gt;\r\n                &lt;p&gt;This was created using cfn-init&lt;\/p&gt;\r\n              mode: '000644'\r\n          commands:\r\n            hello:\r\n              command: \"echo 'hello world'\"\r\n          services:\r\n            sysvinit:\r\n              httpd:\r\n                enabled: 'true'\r\n                ensureRunning: 'true'\r\n\r\n  # our EC2 security group\r\n  SSHSecurityGroup:\r\n    Type: AWS::EC2::SecurityGroup\r\n    Properties:\r\n      GroupDescription: SSH and HTTP\r\n      SecurityGroupIngress:\r\n      - CidrIp: 0.0.0.0\/0\r\n        FromPort: 22\r\n        IpProtocol: tcp\r\n        ToPort: 22\r\n      - CidrIp: 0.0.0.0\/0\r\n        FromPort: 80\r\n        IpProtocol: tcp\r\n        ToPort: 80<\/pre>\n<p>Cfn init is more readable way to install packages and modify the files on the system than user data way.<\/p>\n<p>Let&#8217;s create a CfnInitExample stack:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4193 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation035.jpg\" alt=\"\" width=\"776\" height=\"697\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation035.jpg 776w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation035-300x269.jpg 300w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation035-768x690.jpg 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/p>\n<p><code>Next -&gt;<\/code><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-4194\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation036.jpg\" alt=\"\" width=\"772\" height=\"479\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation036.jpg 772w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation036-300x186.jpg 300w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation036-768x477.jpg 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/p>\n<p><code>Next -&gt; Create stack<\/code><\/p>\n<p>Now we can go to the url of the new EC2 instance:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4196 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation038.jpg\" alt=\"\" width=\"683\" height=\"172\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation038.jpg 683w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation038-300x76.jpg 300w\" sizes=\"(max-width: 683px) 100vw, 683px\" \/><\/p>\n<p>Now let&#8217;s ssh to the EC2 instance:<\/p>\n<pre class=\"lang:default decode:true\">sudo cat \/var\/log\/cloud-init-output.log<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4198 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation039.jpg\" alt=\"\" width=\"1273\" height=\"573\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation039.jpg 1273w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation039-300x135.jpg 300w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation039-1024x461.jpg 1024w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation039-768x346.jpg 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/p>\n<p>And<\/p>\n<pre class=\"lang:default decode:true\">[ec2-user@ip-172-31-24-120 ~]$ sudo cat \/var\/log\/cfn-init.log\r\n2021-06-01 12:50:36,880 [INFO] -----------------------Starting build-----------------------\r\n2021-06-01 12:50:36,881 [INFO] Running configSets: default\r\n2021-06-01 12:50:36,883 [INFO] Running configSet default\r\n2021-06-01 12:50:36,884 [INFO] Running config config\r\n2021-06-01 12:50:48,881 [INFO] Yum installed ['httpd']\r\n2021-06-01 12:50:48,888 [INFO] Command hello succeeded\r\n2021-06-01 12:50:49,012 [INFO] enabled service httpd\r\n2021-06-01 12:50:49,188 [INFO] Started httpd successfully\r\n2021-06-01 12:50:49,189 [INFO] ConfigSets completed\r\n2021-06-01 12:50:49,189 [INFO] -----------------------Build complete-----------------------<\/pre>\n<p>All the logs:<\/p>\n<pre class=\"lang:default decode:true \">[ec2-user@ip-172-31-24-120 ~]$ ls \/var\/log\r\namazon boot.log cfn-init-cmd.log cfn-wire.log cloud-init.log cron grubby_prune_debug journal maillog sa spooler wtmp\r\naudit btmp cfn-init.log chrony cloud-init-output.log dmesg httpd lastlog messages secure tallylog yum.log<\/pre>\n<p>If we need troubleshot:<\/p>\n<pre class=\"lang:default decode:true\">[ec2-user@ip-172-31-24-120 ~]$ sudo cat \/var\/log\/cfn-init.log\r\n2021-06-01 12:50:36,880 [INFO] -----------------------Starting build-----------------------\r\n2021-06-01 12:50:36,881 [INFO] Running configSets: default\r\n2021-06-01 12:50:36,883 [INFO] Running configSet default\r\n2021-06-01 12:50:36,884 [INFO] Running config config\r\n2021-06-01 12:50:48,881 [INFO] Yum installed ['httpd']\r\n2021-06-01 12:50:48,888 [INFO] Command hello succeeded\r\n2021-06-01 12:50:49,012 [INFO] enabled service httpd\r\n2021-06-01 12:50:49,188 [INFO] Started httpd successfully\r\n2021-06-01 12:50:49,189 [INFO] ConfigSets completed\r\n2021-06-01 12:50:49,189 [INFO] -----------------------Build complete-----------------------<\/pre>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #3366ff;\">cfn signal &amp; wait conditions<\/span><\/p>\n<ul>\n<li>We still don&#8217;t know how to tell CloudFormation that the EC2 instance got properly configured after a cfn-init<\/li>\n<li>For this, we can use the cfn-signal script!\n<ul>\n<li>We run cfn-signal right after cfn-init<\/li>\n<li>Tell CloudFormation service to keep on going or fail<\/li>\n<\/ul>\n<\/li>\n<li>We need to define WaitCondition:\n<ul>\n<li>Block the template until it receives a signal from cfn-signal<\/li>\n<li>We attach a CreationPolicy (also works on EC2, ASG)<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4203 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation040.jpg\" alt=\"\" width=\"747\" height=\"846\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation040.jpg 747w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation040-265x300.jpg 265w\" sizes=\"(max-width: 706px) 89vw, (max-width: 767px) 82vw, 740px\" \/><\/p>\n<p>Consider such a tamplate:<\/p>\n<pre class=\"lang:default decode:true \">---\r\nParameters:\r\n  SSHKey:\r\n    Type: AWS::EC2::KeyPair::KeyName\r\n    Description: Name of an existing EC2 KeyPair to enable SSH access to the instance\r\n\r\nResources:\r\n  MyInstance:\r\n    Type: AWS::EC2::Instance\r\n    Properties:\r\n      AvailabilityZone: eu-central-1a\r\n      ImageId: ami-00a205cb8e06c3c4e\r\n      InstanceType: t2.micro\r\n      KeyName: !Ref SSHKey\r\n      SecurityGroups:\r\n        - !Ref SSHSecurityGroup\r\n      # we install our web server with user data\r\n      UserData: \r\n        Fn::Base64:\r\n          !Sub |\r\n            #!\/bin\/bash -xe\r\n            # Get the latest CloudFormation package\r\n            yum update -y aws-cfn-bootstrap\r\n            # Start cfn-init\r\n            \/opt\/aws\/bin\/cfn-init -s ${AWS::StackId} -r MyInstance --region ${AWS::Region}\r\n            # Start cfn-signal to the wait condition\r\n            \/opt\/aws\/bin\/cfn-signal -e $? --stack ${AWS::StackId} --resource SampleWaitCondition --region ${AWS::Region}\r\n    Metadata:\r\n      Comment: Install a simple Apache HTTP page\r\n      AWS::CloudFormation::Init:\r\n        config:\r\n          packages:\r\n            yum:\r\n              httpd: []\r\n          files:\r\n            \"\/var\/www\/html\/index.html\":\r\n              content: |\r\n                &lt;h1&gt;Hello World from EC2 instance!&lt;\/h1&gt;\r\n                &lt;p&gt;This was created using cfn-init&lt;\/p&gt;\r\n              mode: '000644'\r\n          commands:\r\n            hello:\r\n              command: \"echo 'hello world'\"\r\n          services:\r\n            sysvinit:\r\n              httpd:\r\n                enabled: 'true'\r\n                ensureRunning: 'true'\r\n\r\n  SampleWaitCondition:\r\n    CreationPolicy:\r\n      ResourceSignal:\r\n        Timeout: PT2M\r\n        Count: 1\r\n    Type: AWS::CloudFormation::WaitCondition\r\n\r\n  # our EC2 security group\r\n  SSHSecurityGroup:\r\n    Type: AWS::EC2::SecurityGroup\r\n    Properties:\r\n      GroupDescription: SSH and HTTP\r\n      SecurityGroupIngress:\r\n      - CidrIp: 0.0.0.0\/0\r\n        FromPort: 22\r\n        IpProtocol: tcp\r\n        ToPort: 22\r\n      - CidrIp: 0.0.0.0\/0\r\n        FromPort: 80\r\n        IpProtocol: tcp\r\n        ToPort: 80\r\n\r\n<\/pre>\n<p>Using this template let&#8217;s create a stack:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4204 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation042.jpg\" alt=\"\" width=\"811\" height=\"479\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation042.jpg 811w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation042-300x177.jpg 300w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation042-768x454.jpg 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/p>\n<p><code>Next -&gt; Create stack<\/code><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-4208\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation043-1.jpg\" alt=\"\" width=\"908\" height=\"568\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation043-1.jpg 908w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation043-1-300x188.jpg 300w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation043-1-768x480.jpg 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #3366ff;\">cfn-signal failures troubleshooting<\/span><\/p>\n<p>Wait Condition Didn&#8217;t Receive the Required Number of Signals from an Amazon EC2 Instance<\/p>\n<ul>\n<li>Ensure that the AMI you&#8217;re using has the AWS CloudFormation helper scripts installed. If the AMI doesn&#8217;t include the helper scripts, you can also cownload them to your instance.<\/li>\n<li>Verify that the <code>cfn-init<\/code> &amp; <code>cfn-signal<\/code> command was successfully run on the instance. You can view logs, such as <code>\/var\/log\/cloud-init.log<\/code> or <code>\/var\/log\/cfn-init.log<\/code>, to help you debug the instance launch.<\/li>\n<li>You can retrieve the logs by logging in to your instance, but you must disable rollback on failure or else AWS-CloudFormation deletes the instance after your stack fails to create.<\/li>\n<li>Verify that the instance has a connection to the Internet. If the instance is in a VPC, the instance should be able to connect to the Internet through a NAT device if it&#8217;s is in a private subnet or through an Internet gateway if it&#8217;s in a public subnet<\/li>\n<li>For example, run: <code>curl -I https:\/\/aws.amazon.com<\/code><\/li>\n<\/ul>\n<p>Consider a following yml tamplate:<\/p>\n<pre class=\"lang:default decode:true\">---\r\nParameters:\r\n  SSHKey:\r\n    Type: AWS::EC2::KeyPair::KeyName\r\n    Description: Name of an existing EC2 KeyPair to enable SSH access to the instance\r\n\r\nResources:\r\n  MyInstance:\r\n    Type: AWS::EC2::Instance\r\n    Properties:\r\n      AvailabilityZone: eu-central-1a\r\n      ImageId: ami-00a205cb8e06c3c4e\r\n      InstanceType: t2.micro\r\n      KeyName: !Ref SSHKey\r\n      SecurityGroups:\r\n        - !Ref SSHSecurityGroup\r\n      # we install our web server with user data\r\n      UserData: \r\n        Fn::Base64:\r\n          !Sub |\r\n            #!\/bin\/bash -xe\r\n            # Get the latest CloudFormation package\r\n            yum update -y aws-cfn-bootstrap\r\n            # Start cfn-init\r\n            \/opt\/aws\/bin\/cfn-init -s ${AWS::StackId} -r MyInstance --region ${AWS::Region}\r\n            # Start cfn-signal to the wait condition\r\n            \/opt\/aws\/bin\/cfn-signal -e $? --stack ${AWS::StackId} --resource SampleWaitCondition --region ${AWS::Region}\r\n    Metadata:\r\n      Comment: Install a simple Apache HTTP page\r\n      AWS::CloudFormation::Init:\r\n        config:\r\n          packages:\r\n            yum:\r\n              httpd: []\r\n          files:\r\n            \"\/var\/www\/html\/index.html\":\r\n              content: |\r\n                &lt;h1&gt;Hello World from EC2 instance!&lt;\/h1&gt;\r\n                &lt;p&gt;This was created using cfn-init&lt;\/p&gt;\r\n              mode: '000644'\r\n          commands:\r\n            hello:\r\n              command: \"echo 'boom' &amp;&amp; exit 1\"\r\n          services:\r\n            sysvinit:\r\n              httpd:\r\n                enabled: 'true'\r\n                ensureRunning: 'true'\r\n\r\n  SampleWaitCondition:\r\n    CreationPolicy:\r\n      ResourceSignal:\r\n        Timeout: PT1M\r\n        Count: 1\r\n    Type: AWS::CloudFormation::WaitCondition\r\n\r\n  # our EC2 security group\r\n  SSHSecurityGroup:\r\n    Type: AWS::EC2::SecurityGroup\r\n    Properties:\r\n      GroupDescription: SSH and HTTP\r\n      SecurityGroupIngress:\r\n      - CidrIp: 0.0.0.0\/0\r\n        FromPort: 22\r\n        IpProtocol: tcp\r\n        ToPort: 22\r\n      - CidrIp: 0.0.0.0\/0\r\n        FromPort: 80\r\n        IpProtocol: tcp\r\n        ToPort: 80\r\n\r\n<\/pre>\n<p>Command<\/p>\n<pre class=\"lang:default decode:true \">hello: command: \"echo 'boom' &amp;&amp; exit 1\"<\/pre>\n<p>will exit with error 1. 1 is bad exit code, good is 0.\u00a0 This will directly trigger cfn-init failure.<\/p>\n<p>Let&#8217;s create a stack:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4212 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation045.jpg\" alt=\"\" width=\"835\" height=\"683\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation045.jpg 835w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation045-300x245.jpg 300w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation045-768x628.jpg 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/p>\n<p><code>Next<\/code><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4213 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation046.jpg\" alt=\"\" width=\"828\" height=\"481\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation046.jpg 828w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation046-300x174.jpg 300w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation046-768x446.jpg 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/p>\n<p><code>Next -&gt; Create stack<\/code><\/p>\n<p>And we see &#8220;<span title=\"Failed to receive 1 resource signal(s) within the specified duration\">Failed to receive 1 resource signal(s) within the specified duration<\/span>&#8221;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4214 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation047.jpg\" alt=\"\" width=\"932\" height=\"781\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation047.jpg 932w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation047-300x251.jpg 300w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation047-768x644.jpg 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/p>\n<p>As the result all the stack has been roll backed.<\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #3366ff;\"> cfn-hup &amp; cfn-metadata<\/span><\/p>\n<p>We have such a yaml template:<\/p>\n<pre class=\"lang:default decode:true \">AWSTemplateFormatVersion: '2010-09-09'\r\nDescription: AWS CloudFormation Sample Template for CFN Init\r\nParameters:\r\n  KeyName:\r\n    Description: Name of an existing EC2 KeyPair to enable SSH access to the instances\r\n    Type: AWS::EC2::KeyPair::KeyName\r\n    ConstraintDescription: must be the name of an existing EC2 KeyPair.\r\n  \r\n  LatestLinuxAmiId:\r\n    Type: 'AWS::SSM::Parameter::Value&lt;AWS::EC2::Image::Id&gt;'\r\n    Default: '\/aws\/service\/ami-amazon-linux-latest\/amzn2-ami-hvm-x86_64-gp2'\r\n\r\n  WelcomeMessage:\r\n    Type: String\r\n    Default: \"Hello World\"\r\n    \r\nResources:\r\n  WebServerSecurityGroup:\r\n    Type: AWS::EC2::SecurityGroup\r\n    Properties:\r\n      GroupDescription: Enable HTTP access via port 80 and SSH access via port 22\r\n      SecurityGroupIngress:\r\n      - IpProtocol: tcp\r\n        FromPort: '80'\r\n        ToPort: '80'\r\n        CidrIp: 0.0.0.0\/0\r\n      - IpProtocol: tcp\r\n        FromPort: '22'\r\n        ToPort: '22'\r\n        CidrIp: 0.0.0.0\/0\r\n\r\n  WebServerHost:\r\n    Type: AWS::EC2::Instance\r\n    Metadata:\r\n      Comment: Install a simple PHP application\r\n      AWS::CloudFormation::Init:\r\n        config:\r\n          packages:\r\n            yum:\r\n              httpd: []\r\n              php: []\r\n          groups:\r\n            apache: {}\r\n          users:\r\n            \"apache\":\r\n              groups:\r\n                - \"apache\"\r\n          sources:\r\n            \"\/home\/ec2-user\/aws-cli\": \"https:\/\/github.com\/aws\/aws-cli\/tarball\/master\"\r\n          files:\r\n            \"\/var\/www\/html\/index.html\":\r\n              content: !Sub |\r\n                &lt;h1&gt;${WelcomeMessage} from ${AWS::StackName}&lt;\/h1&gt;\r\n              mode: '000644'\r\n              owner: apache\r\n              group: apache\r\n            # The cfn-hup.conf file stores the name of the stack and the AWS credentials that the cfn-hup daemon targets.\r\n            \"\/etc\/cfn\/cfn-hup.conf\":\r\n              content: !Sub |\r\n                [main]\r\n                stack=${AWS::StackId}\r\n                region=${AWS::Region}\r\n                # The interval used to check for changes to the resource metadata in minutes. Default is 15\r\n                interval=2\r\n              mode: \"000400\"\r\n              owner: \"root\"\r\n              group: \"root\"\r\n            # The user actions that the cfn-hup daemon calls periodically are defined in the hooks.conf configuration file.\r\n            # To support composition of several applications deploying change notification hooks, cfn-hup supports a directory named hooks.d that is located in the hooks configuration directory. You can place one or more additional hooks configuration files in the hooks.d directory. The additional hooks files must use the same layout as the hooks.conf file.\r\n            \"\/etc\/cfn\/hooks.d\/cfn-auto-reloader.conf\":\r\n              content: !Sub |\r\n                [cfn-auto-reloader-hook]\r\n                triggers=post.update\r\n                path=Resources.WebServerHost.Metadata.AWS::CloudFormation::Init\r\n                action=\/opt\/aws\/bin\/cfn-init -v --stack ${AWS::StackName} --resource WebServerHost --region ${AWS::Region}\r\n              mode: \"000400\"\r\n              owner: \"root\"\r\n              group: \"root\"\r\n          services:\r\n            sysvinit:\r\n              httpd:\r\n                enabled: 'true'\r\n                ensureRunning: 'true'\r\n    CreationPolicy:\r\n      ResourceSignal:\r\n        Timeout: PT5M\r\n    Properties:\r\n      ImageId: !Ref LatestLinuxAmiId\r\n      KeyName:\r\n        Ref: KeyName\r\n      InstanceType: t2.micro\r\n      SecurityGroups:\r\n      - Ref: WebServerSecurityGroup\r\n      UserData:\r\n        \"Fn::Base64\":\r\n          !Sub |\r\n            #!\/bin\/bash -xe\r\n            # Get the latest CloudFormation package\r\n            yum update -y aws-cfn-bootstrap\r\n            # Start cfn-init\r\n            \/opt\/aws\/bin\/cfn-init -s ${AWS::StackId} -r WebServerHost --region ${AWS::Region} || error_exit 'Failed to run cfn-init'\r\n            # Start up the cfn-hup daemon to listen for changes to the EC2 instance metadata\r\n            \/opt\/aws\/bin\/cfn-hup || error_exit 'Failed to start cfn-hup'\r\n            # All done so signal success\r\n            \/opt\/aws\/bin\/cfn-signal -e $? --stack ${AWS::StackId} --resource WebServerHost --region ${AWS::Region}\r\n\r\nOutputs:\r\n  InstanceId:\r\n    Description: The instance ID of the web server\r\n    Value:\r\n      Ref: WebServerHost\r\n  WebsiteURL:\r\n    Value:\r\n      !Sub 'http:\/\/${WebServerHost.PublicDnsName}'\r\n    Description: URL for newly created LAMP stack\r\n  PublicIP:\r\n    Description: Public IP address of the web server\r\n    Value:\r\n      !GetAtt WebServerHost.PublicIp\r\n\r\n\r\n# Get metadata (change the region accordingly)\r\n# \/opt\/aws\/bin\/cfn-get-metadata --stack CfnHupDemo --resource WebServerHost --region eu-west-1<\/pre>\n<p>Let&#8217;s create a stack:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4426 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation67.jpg\" alt=\"\" width=\"737\" height=\"737\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation67.jpg 737w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation67-300x300.jpg 300w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation67-150x150.jpg 150w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation67-100x100.jpg 100w\" sizes=\"(max-width: 737px) 100vw, 737px\" \/><\/p>\n<p><code>Next -&gt;<\/code><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4428 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation68-1.jpg\" alt=\"\" width=\"740\" height=\"671\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation68-1.jpg 740w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation68-1-300x272.jpg 300w\" sizes=\"(max-width: 706px) 89vw, (max-width: 767px) 82vw, 740px\" \/><\/p>\n<p><code>Next -&gt; Create stack<\/code><\/p>\n<p>The stack has successfully been created<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4431 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation69.jpg\" alt=\"\" width=\"790\" height=\"706\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation69.jpg 790w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation69-300x268.jpg 300w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation69-768x686.jpg 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/p>\n<p>The http server on EC2 instance is up:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4432 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation70.jpg\" alt=\"\" width=\"657\" height=\"137\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation70.jpg 657w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation70-300x63.jpg 300w\" sizes=\"(max-width: 657px) 100vw, 657px\" \/><\/p>\n<p>To get metatdata from EC2 instance let&#8217;s connect to it and run command:<\/p>\n<pre class=\"lang:default decode:true \">\/opt\/aws\/bin\/cfn-get-metadata --stack CfnHupDemo --resource WebServerHost --region eu-central-1<\/pre>\n<p>The output:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4433 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation71.jpg\" alt=\"\" width=\"1355\" height=\"700\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation71.jpg 1355w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation71-300x155.jpg 300w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation71-1024x529.jpg 1024w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation71-768x397.jpg 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/p>\n<p>Now let&#8217;s update the parameter of stack:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-4435 size-full\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation72-1.jpg\" alt=\"\" width=\"708\" height=\"487\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation72-1.jpg 708w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation72-1-300x206.jpg 300w\" sizes=\"(max-width: 708px) 100vw, 708px\" \/><\/p>\n<p><code>Next -&gt; Next -&gt; Update stack<\/code><\/p>\n<p>The meadata of EC2 instance has been changed:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4436 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation73.jpg\" alt=\"\" width=\"1343\" height=\"698\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation73.jpg 1343w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation73-300x156.jpg 300w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation73-1024x532.jpg 1024w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation73-768x399.jpg 768w\" sizes=\"(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/p>\n<p>But the content of index.html bas been changed after 2 minutues:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-4437 aligncenter\" src=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation74.jpg\" alt=\"\" width=\"692\" height=\"132\" srcset=\"http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation74.jpg 692w, http:\/\/miro.borodziuk.eu\/wp-content\/uploads\/CloudFormation74-300x57.jpg 300w\" sizes=\"(max-width: 692px) 100vw, 692px\" \/><\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":4218,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[89],"tags":[],"_links":{"self":[{"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/posts\/4217"}],"collection":[{"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/comments?post=4217"}],"version-history":[{"count":9,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/posts\/4217\/revisions"}],"predecessor-version":[{"id":4438,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/posts\/4217\/revisions\/4438"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/media\/4218"}],"wp:attachment":[{"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/media?parent=4217"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/categories?post=4217"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/tags?post=4217"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}