When launching an Amazon EC2 instance, you often need to automate the installation of packages, configuration of services, and setup of applications. AWS provides two primary approaches for this: User Data scripts and cfn-init (CloudFormation helper script). Each has its own strengths and limitations. In this blog post, we’ll explore both, compare their pros and cons, and explain how you can use cfn-hup to automate in-place updates.


User Data for EC2 Bootstrapping

What is User Data?

User Data is a script that runs when an EC2 instance is first launched. It allows for basic configuration and setup tasks such as installing software packages, modifying files, and starting services.

Pros of User Data:

  • Simplicity: User Data scripts are easy to implement and require no additional AWS services.
  • Flexibility: It allows dynamic customization at launch without the need for pre-configured Amazon Machine Images (AMIs).
  • No CloudFormation Dependency: You can use it outside of CloudFormation, making it a general-purpose solution.

Cons of User Data:

  • One-Time Execution: Runs only at the first boot cycle, so updates require instance replacement or manual execution.
  • No Built-In Update Mechanism: If you modify the script in CloudFormation, the entire instance must be replaced.
  • Limited Debugging Features: Logs are stored in /var/log/cloud-init.log, but tracking failures can be challenging.

Example User Data Script:

#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
echo "<h1>Welcome to My Server</h1>" > /var/www/html/index.html

cfn-init: The Advanced Alternative

What is cfn-init?

cfn-init is a helper script that processes the AWS::CloudFormation::Init metadata section in a CloudFormation template. It enables declarative instance configuration, including package installations, file management, and service setup.

Pros of cfn-init:

  • In-Place Updates: Configuration changes can be applied without replacing the instance.
  • Structured Configuration: Uses a declarative approach with CloudFormation metadata for better manageability.
  • Better Debugging: Logs are stored in /var/log/cfn-init.log, making it easier to diagnose issues.

Cons of cfn-init:

  • Complexity: Requires a deeper understanding of CloudFormation metadata.
  • CloudFormation Dependency: It can only be used in CloudFormation-based deployments.

Example cfn-init Configuration and Update Process:

Resources:
  MyInstance:
    Type: AWS::EC2::Instance
    DependsOn:
         - AttachRouteTable
    Metadata:
      AWS::CloudFormation::Init:
        config:
          packages:
            yum:
              httpd: []  # Install Apache
          files:
            "/var/www/html/index.html":
              content: !Sub |
                <h1> ${Message} </h1>
              mode: '000644'
              owner: root
              group: root
          services:
            sysvinit:
              httpd:
                enabled: true
                ensureRunning: true
    Properties:
      ImageId: !Ref ImageId
      InstanceType: !Ref EC2InstanceType
      KeyName: !Ref KeyName
      SecurityGroupIds:
        - !GetAtt MyEC2SecurityGroup.GroupId
      SubnetId: !Ref PublicSubnet
      UserData:
        Fn::Base64:
          !Sub |
            #!/bin/bash -xe
            /opt/aws/bin/cfn-init --stack ${AWS::StackName} --resource MyInstance --region ${AWS::Region}

You can see the full Cloudformation template here!

How Updates Work

When an update is made to the metadata under AWS::CloudFormation::Init, it does not automatically apply to the running instance. Instead, you must trigger cfn-init again to apply the changes.

To do this, run the following command manually on the instance:

/opt/aws/bin/cfn-init -v --stack MyStack --resource MyInstance --region us-east-1

Alternatively, you can automate this process using cfn-hup, which continuously monitors for metadata changes and applies them dynamically. This ensures your instance stays updated without requiring manual intervention.


Using cfn-hup for Automatic In-Place Updates

What is cfn-hup?

cfn-hup is a daemon that monitors CloudFormation metadata for changes and automatically applies updates to an instance using cfn-init. This means you can modify configurations without rebooting or replacing the instance.

Steps to Use cfn-hup

1. Install and Configure cfn-hup

Change the template as below to include the configuration for setting upcfn-hup:

Resources:
  MyInstance:
    Type: AWS::EC2::Instance
    DependsOn:
      - AttachRouteTable
    Metadata:
      AWS::CloudFormation::Init:
        config:
          packages:
            yum:
              httpd: []  # Install Apache
          files:
            "/var/www/html/index.html":
              content: !Sub |
                <h1> ${Message} </h1>
              mode: '000644'
              owner: root
              group: root
            "/etc/cfn/cfn-hup.conf":
              content: !Sub |
                [main]
                stack=${AWS::StackId}
                region=${AWS::Region}
                interval=1
              mode: '000400'
              owner: root
              group: root
            "/etc/cfn/hooks.d/cfn-auto-reloader.conf":
              content: !Sub |
                [cfn-auto-reloader-hook]
                triggers=post.update
                path=Resources.MyInstance.Metadata.AWS::CloudFormation::Init
                action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource MyInstance --region ${AWS::Region}
                runas=root
              mode: '000400'
              owner: root
              group: root
          services:
            sysvinit:
              httpd:
                enabled: true
                ensureRunning: true
              cfn-hup:
                enabled: true
                ensureRunning: true
                files:
                  - /etc/cfn/cfn-hup.conf
                  - /etc/cfn/hooks.d/cfn-auto-reloader.conf
    Properties:
      ImageId: !Ref ImageId
      InstanceType: !Ref EC2InstanceType
      KeyName: !Ref KeyName
      SecurityGroupIds:
        - !GetAtt MyEC2SecurityGroup.GroupId
      SubnetId: !Ref PublicSubnet
      UserData:
        Fn::Base64:
          !Sub |
            #!/bin/bash -xe
            yum install -y aws-cfn-bootstrap
            /opt/aws/bin/cfn-init --stack ${AWS::StackName} --resource MyInstance --region ${AWS::Region}

You can see the full Cloudformation template here!

This new template ensures that cfn-hup starts automatically with the instance as below:

services:
  sysvinit:
    cfn-hup:
      enabled: true
      ensureRunning: true
      files:
        - "/etc/cfn/cfn-hup.conf"
        - "/etc/cfn/hooks.d/cfn-auto-reload.conf"

3. Deploy and Modify Configuration

When you update the metadata (e.g., changing the website content), cfn-hup detects the change and automatically applies the update without replacing the instance.

For example, update your CloudFormation stack as below:

aws cloudformation update-stack --stack-name MyStack --use-previous-template --parameters  "ParameterKey=Message,ParameterValue=This is new"

Within one minute, cfn-hup will apply the new configuration without requiring a reboot or instance replacement and the webpage will show the sentence “This is new”!


Conclusion: Which One Should You Choose?

Feature User Data cfn-init
Simplicity ✅ Easy ❌ More Complex
In-Place Updates ❌ No ✅ Yes
Structured Config ❌ No ✅ Yes
Debugging Logs ❌ Limited ✅ Detailed
AWS CloudFormation Dependent ❌ No ✅ Yes

When to use User Data: If you need a simple, one-time initialization script.

When to use cfn-init: If you need structured configuration management, in-place updates, and better debugging.

For complex AWS deployments, cfn-init + cfn-hup is a more powerful and scalable solution.