Cloud

Install AWS CloudFormation Helper Scripts on Linux

AWS CloudFormation helper scripts are a set of Python-based utilities that automate software installation and service configuration on EC2 instances launched through CloudFormation stacks. They bridge the gap between declaring resources in a template and actually configuring the operating system inside those instances.

Original content from computingforgeeks.com - post 58130

The four helper scripts – cfn-init, cfn-signal, cfn-hup, and cfn-get-metadata – handle everything from installing packages and writing config files to signaling stack completion and detecting live metadata changes. This guide covers installation on any Linux distribution (RHEL, Ubuntu, Debian, Amazon Linux), template configuration with AWS::CloudFormation::Init, signaling with CreationPolicy, and troubleshooting common issues.

Prerequisites

  • An AWS account with permissions to create CloudFormation stacks and EC2 instances
  • EC2 instances running a supported Linux distribution – Amazon Linux 2/2023, RHEL 9/10, Rocky Linux 9/10, AlmaLinux 9/10, Ubuntu 22.04/24.04, or Debian 12/13
  • Python 3.4 or later installed on non-Amazon Linux instances
  • AWS CLI installed and configured (for stack deployment and testing)
  • An IAM role attached to EC2 instances with permissions to read CloudFormation stack metadata
  • Security group allowing outbound HTTPS (port 443) to reach AWS API endpoints

Step 1: Install CloudFormation Helper Scripts on Linux

The helper scripts ship in the aws-cfn-bootstrap package. Installation varies by distribution. On Amazon Linux, they come preinstalled at /opt/aws/bin. For all other Linux distributions, install them manually using one of the methods below.

Method 1: Install from tarball (all Linux distributions)

This method works on any Linux system with Python 3. Download the latest Python 3 compatible package directly from AWS:

curl -O https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz

Install the package using pip. If pip is not installed, install it first with your distribution’s package manager (python3-pip on most systems):

sudo pip3 install aws-cfn-bootstrap-py3-latest.tar.gz

Create the standard /opt/aws/bin directory and symlink the helper scripts so CloudFormation templates can find them at the expected path:

sudo mkdir -p /opt/aws/bin
sudo ln -sf /usr/local/bin/cfn-init /opt/aws/bin/cfn-init
sudo ln -sf /usr/local/bin/cfn-signal /opt/aws/bin/cfn-signal
sudo ln -sf /usr/local/bin/cfn-hup /opt/aws/bin/cfn-hup
sudo ln -sf /usr/local/bin/cfn-get-metadata /opt/aws/bin/cfn-get-metadata

Verify the installation by checking the version:

cfn-init --version

You should see the aws-cfn-bootstrap version printed:

cfn-init 2.0

Method 2: Install on RHEL/Rocky/AlmaLinux (RPM-based)

On RHEL-family distributions, make sure Python 3 and pip are available, then install from the tarball. The process is the same as Method 1, but ensure the prerequisites are in place first:

sudo dnf install -y python3 python3-pip
sudo pip3 install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz

Create the symlinks:

sudo mkdir -p /opt/aws/bin
sudo ln -sf /usr/local/bin/cfn-init /opt/aws/bin/cfn-init
sudo ln -sf /usr/local/bin/cfn-signal /opt/aws/bin/cfn-signal
sudo ln -sf /usr/local/bin/cfn-hup /opt/aws/bin/cfn-hup
sudo ln -sf /usr/local/bin/cfn-get-metadata /opt/aws/bin/cfn-get-metadata

Method 3: Install on Ubuntu/Debian (DEB-based)

On Debian-family distributions, install Python 3 and pip, then use the same tarball method:

sudo apt update
sudo apt install -y python3 python3-pip
sudo pip3 install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz

Set up the cfn-hup init script for Ubuntu/Debian systems:

sudo mkdir -p /opt/aws/bin
sudo ln -sf /usr/local/bin/cfn-init /opt/aws/bin/cfn-init
sudo ln -sf /usr/local/bin/cfn-signal /opt/aws/bin/cfn-signal
sudo ln -sf /usr/local/bin/cfn-hup /opt/aws/bin/cfn-hup
sudo ln -sf /usr/local/bin/cfn-get-metadata /opt/aws/bin/cfn-get-metadata

Method 4: Install on Amazon Linux (preinstalled)

On Amazon Linux 2 and Amazon Linux 2023, the helper scripts are already installed. To ensure you have the latest version, run:

sudo yum install -y aws-cfn-bootstrap

The scripts are available at /opt/aws/bin/ and ready to use immediately.

Step 2: Configure cfn-init in a CloudFormation Template

The cfn-init script reads the AWS::CloudFormation::Init metadata key from your template and executes the instructions. This is the primary way to declaratively configure an EC2 instance – installing packages, writing files, and starting services without writing shell scripts.

Here is a template snippet that installs and configures Nginx using cfn-init:

Resources:
  WebServer:
    Type: AWS::EC2::Instance
    Metadata:
      AWS::CloudFormation::Init:
        config:
          packages:
            yum:
              nginx: []
          files:
            /etc/nginx/conf.d/app.conf:
              content: |
                server {
                    listen 80;
                    server_name _;
                    root /var/www/html;
                    index index.html;
                }
              mode: "000644"
              owner: root
              group: root
            /var/www/html/index.html:
              content: |
                <h1>CloudFormation deployed this server</h1>
              mode: "000644"
              owner: root
              group: root
          services:
            sysvinit:
              nginx:
                enabled: true
                ensureRunning: true
                files:
                  - /etc/nginx/conf.d/app.conf

The packages section installs packages using the system package manager. The files section writes configuration files with specified ownership and permissions. The services section enables and starts services, with automatic restart when watched files change.

To trigger cfn-init, add it to the instance’s UserData:

    Properties:
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash -xe
          /opt/aws/bin/cfn-init -v \
            --stack ${AWS::StackName} \
            --resource WebServer \
            --region ${AWS::Region}

The --resource flag must match the logical name of the resource that contains the AWS::CloudFormation::Init metadata (in this case, WebServer).

Step 3: Use cfn-signal with WaitCondition

By default, CloudFormation marks an EC2 instance as CREATE_COMPLETE as soon as the instance launches – not when your software is actually configured. The cfn-signal script tells CloudFormation to wait until your setup finishes before proceeding.

Add a WaitCondition and WaitConditionHandle to your template:

  WaitHandle:
    Type: AWS::CloudFormation::WaitConditionHandle

  WaitCondition:
    Type: AWS::CloudFormation::WaitCondition
    DependsOn: WebServer
    Properties:
      Handle: !Ref WaitHandle
      Timeout: "600"
      Count: 1

Then signal success (or failure) at the end of UserData:

      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash -xe
          /opt/aws/bin/cfn-init -v \
            --stack ${AWS::StackName} \
            --resource WebServer \
            --region ${AWS::Region}

          /opt/aws/bin/cfn-signal -e $? \
            --stack ${AWS::StackName} \
            --resource WaitCondition \
            --region ${AWS::Region}

The -e $? flag passes the exit code of the previous command. If cfn-init fails, cfn-signal sends a failure signal, and CloudFormation rolls back the stack. The Timeout value of 600 gives the instance 10 minutes to complete setup before CloudFormation times out.

Step 4: Configure cfn-hup for Live Config Updates

The cfn-hup daemon runs on the instance and polls CloudFormation for metadata changes at a configurable interval. When it detects changes to the template metadata, it runs the actions you define – typically re-running cfn-init to apply updated configuration without replacing the instance.

cfn-hup needs two configuration files. Add them both to the files section of your AWS::CloudFormation::Init metadata:

          files:
            /etc/cfn/cfn-hup.conf:
              content: !Sub |
                [main]
                stack=${AWS::StackId}
                region=${AWS::Region}
                interval=5
              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.WebServer.Metadata.AWS::CloudFormation::Init
                action=/opt/aws/bin/cfn-init -v \
                  --stack ${AWS::StackName} \
                  --resource WebServer \
                  --region ${AWS::Region}
                runas=root
              mode: "000400"
              owner: root
              group: root

The cfn-hup.conf file tells cfn-hup which stack to monitor and how often to check (every 5 minutes in this example). The cfn-auto-reloader.conf hook triggers cfn-init to re-run whenever the Init metadata on the WebServer resource changes.

Add cfn-hup to the services section so it starts automatically and restarts if its config changes:

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

Step 5: Use CreationPolicy Instead of WaitCondition

CreationPolicy is the modern, simpler alternative to WaitCondition for signaling instance readiness. It attaches directly to the EC2 resource – no separate WaitConditionHandle needed. AWS recommends CreationPolicy for new templates.

Add the CreationPolicy attribute to your EC2 instance resource:

  WebServer:
    Type: AWS::EC2::Instance
    CreationPolicy:
      ResourceSignal:
        Count: 1
        Timeout: PT10M
    Metadata:
      AWS::CloudFormation::Init:
        config:
          packages:
            yum:
              nginx: []

The Timeout: PT10M uses ISO 8601 duration format – 10 minutes in this case. The Count: 1 means CloudFormation waits for exactly one success signal before marking the resource as complete.

Signal the resource directly in UserData:

      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash -xe
          /opt/aws/bin/cfn-init -v \
            --stack ${AWS::StackName} \
            --resource WebServer \
            --region ${AWS::Region}

          /opt/aws/bin/cfn-signal -e $? \
            --stack ${AWS::StackName} \
            --resource WebServer \
            --region ${AWS::Region}

Notice the difference from WaitCondition – with CreationPolicy, cfn-signal points to the instance resource itself (WebServer), not a separate WaitCondition resource.

Step 6: Complete CloudFormation Template with Helper Scripts

Here is a full working template that combines all the helper scripts – cfn-init for configuration, cfn-signal with CreationPolicy for completion signaling, and cfn-hup for ongoing updates. This template deploys an Nginx web server on an Amazon Linux 2023 instance:

AWSTemplateFormatVersion: "2010-09-09"
Description: EC2 instance with CloudFormation helper scripts

Parameters:
  InstanceType:
    Type: String
    Default: t3.micro
  KeyName:
    Type: AWS::EC2::KeyPair::KeyName
    Description: SSH key pair name
  VpcId:
    Type: AWS::EC2::VPC::Id
  SubnetId:
    Type: AWS::EC2::Subnet::Id
  LatestAmiId:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64

Resources:
  InstanceSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow HTTP and SSH
      VpcId: !Ref VpcId
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0

  WebServer:
    Type: AWS::EC2::Instance
    CreationPolicy:
      ResourceSignal:
        Count: 1
        Timeout: PT10M
    Metadata:
      AWS::CloudFormation::Init:
        configSets:
          full_install:
            - install_packages
            - configure_app
            - configure_cfn_hup
        install_packages:
          packages:
            yum:
              nginx: []
        configure_app:
          files:
            /usr/share/nginx/html/index.html:
              content: |
                <h1>Deployed with CloudFormation</h1>
                <p>This server was configured using cfn-init helper scripts.</p>
              mode: "000644"
              owner: root
              group: root
          services:
            sysvinit:
              nginx:
                enabled: true
                ensureRunning: true
        configure_cfn_hup:
          files:
            /etc/cfn/cfn-hup.conf:
              content: !Sub |
                [main]
                stack=${AWS::StackId}
                region=${AWS::Region}
                interval=5
              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.WebServer.Metadata.AWS::CloudFormation::Init
                action=/opt/aws/bin/cfn-init -v \
                  --stack ${AWS::StackName} \
                  --resource WebServer \
                  --configsets full_install \
                  --region ${AWS::Region}
                runas=root
              mode: "000400"
              owner: root
              group: root
          services:
            sysvinit:
              cfn-hup:
                enabled: true
                ensureRunning: true
                files:
                  - /etc/cfn/cfn-hup.conf
                  - /etc/cfn/hooks.d/cfn-auto-reloader.conf
    Properties:
      InstanceType: !Ref InstanceType
      KeyName: !Ref KeyName
      ImageId: !Ref LatestAmiId
      SubnetId: !Ref SubnetId
      SecurityGroupIds:
        - !Ref InstanceSecurityGroup
      IamInstanceProfile: !Ref InstanceProfile
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash -xe
          /opt/aws/bin/cfn-init -v \
            --stack ${AWS::StackName} \
            --resource WebServer \
            --configsets full_install \
            --region ${AWS::Region}

          /opt/aws/bin/cfn-signal -e $? \
            --stack ${AWS::StackName} \
            --resource WebServer \
            --region ${AWS::Region}

  InstanceRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
      Policies:
        - PolicyName: CloudFormationDescribe
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - cloudformation:DescribeStackResource
                  - cloudformation:SignalResource
                Resource: "*"

  InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref InstanceRole

Outputs:
  PublicIP:
    Description: Public IP of the web server
    Value: !GetAtt WebServer.PublicIp
  WebURL:
    Description: URL of the web server
    Value: !Sub http://${WebServer.PublicDnsName}

Deploy this template using the AWS CLI:

aws cloudformation create-stack \
  --stack-name web-server-demo \
  --template-body file://template.yaml \
  --parameters \
    ParameterKey=KeyName,ParameterValue=my-key \
    ParameterKey=VpcId,ParameterValue=vpc-0123456789abcdef0 \
    ParameterKey=SubnetId,ParameterValue=subnet-0123456789abcdef0 \
  --capabilities CAPABILITY_IAM

Monitor the stack creation progress:

aws cloudformation describe-stack-events \
  --stack-name web-server-demo \
  --query "StackEvents[?ResourceStatus=='CREATE_FAILED'].[LogicalResourceId,ResourceStatusReason]" \
  --output table

Step 7: Troubleshoot CloudFormation Helper Scripts

When cfn-init fails, the stack rolls back and you lose SSH access to the instance by default. The most effective troubleshooting approach is to disable rollback during development so you can inspect logs on the failed instance.

Create the stack with rollback disabled:

aws cloudformation create-stack \
  --stack-name debug-stack \
  --template-body file://template.yaml \
  --disable-rollback \
  --capabilities CAPABILITY_IAM

Once you SSH into the instance, the helper scripts write detailed logs to /var/log/. Check these files in order:

View the cfn-init log for package installation and file creation errors:

sudo cat /var/log/cfn-init.log

Check the cfn-init command output log for detailed error messages:

sudo cat /var/log/cfn-init-cmd.log

Review the cloud-init output to see if UserData executed at all:

sudo cat /var/log/cloud-init-output.log

If cfn-hup is not picking up changes, check its log:

sudo cat /var/log/cfn-hup.log

Common issues and fixes:

  • cfn-init not found – the helper scripts are not installed or not symlinked to /opt/aws/bin/. Verify the installation path in UserData
  • Timeout waiting for signal – cfn-signal never ran, usually because cfn-init failed and the script exited before reaching the signal command. Check /var/log/cfn-init.log for the root cause
  • Access denied errors in cfn-init – the EC2 instance IAM role is missing cloudformation:DescribeStackResource or cloudformation:SignalResource permissions
  • Package installation fails – the instance cannot reach package repositories. Check that the security group allows outbound traffic and that the subnet has internet access (NAT gateway or public subnet)
  • cfn-hup not detecting changes – verify the path in the hooks config matches the exact resource path in your template. The path is case-sensitive

Step 8: CloudFormation Helper Scripts Reference

This table summarizes all four CloudFormation helper scripts, what they do, and when to use each one:

ScriptPurposeWhen to Use
cfn-initReads AWS::CloudFormation::Init metadata to install packages, write files, create users, and manage servicesEvery stack that configures EC2 instances – this is the primary helper script
cfn-signalSends a success or failure signal to CloudFormation to indicate resource readinessWith CreationPolicy or WaitCondition to prevent premature CREATE_COMPLETE status
cfn-hupDaemon that polls for metadata changes and runs hooks when updates are detectedLong-running instances that need to pick up template changes without replacement
cfn-get-metadataRetrieves all or part of a resource’s metadata from the CloudFormation stackCustom scripts that need to read stack metadata outside of cfn-init

Key cfn-init flags for reference:

FlagDescription
--stackName or ID of the CloudFormation stack
--resourceLogical name of the resource containing AWS::CloudFormation::Init metadata
--regionAWS region where the stack was created
--configsetsComma-separated list of configSets to run (runs all configs if omitted)
-vVerbose mode – logs detailed output to /var/log/cfn-init.log

Conclusion

CloudFormation helper scripts give you declarative instance configuration that integrates directly with stack lifecycle management. The combination of cfn-init for setup, cfn-signal with CreationPolicy for completion tracking, and cfn-hup for live updates covers the full lifecycle of an EC2 instance managed through CloudFormation.

For production deployments, combine helper scripts with infrastructure-as-code tools for multi-environment management, and use SSM Agent for ongoing instance management without SSH access. Store sensitive configuration values in AWS Systems Manager Parameter Store or Secrets Manager rather than hardcoding them in templates.

Related Articles

Cloud Enable REST API Access in Ceph Object Storage Cloud Remove admin tenant compute quota limits in OpenStack Cloud Integrating CDN with Cloud Services_ Benefits and Best Practices Cloud Create Private Networks in OpenStack using Terraform

Leave a Comment

Press ESC to close