As more and more of my CloudFormation (CF) stacks use a base image and CloudFormation::Init magic, it’s become imperative to have an AMI that has the helper scripts (cfn-signal, cfn-init, etc.) built-in. This isn’t a problem if you use the Amazon Linux AMI, but if you’re playing with things like immutable infrastructure or baking your own custom AMIs for CIS hardening or some other regulatory requirement, it can become a big issue quickly. There’s a little documentation out there on installing the CF helper scripts (https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-helper-scripts-reference.html) but the installation process is not quite so straightforward as one would hope.
The solution to this issue varies depending on your OS. I’ve had no issue with Windows AMIs because the Ec2Config service takes care of everything, but in CentOS and RHEL, there are a few extra steps. I’ll break them down by OS. Note that you may need to search for updated version of things like epel-release to make sure it matches your OS or you’re using the most current version.
This was relatively painless, thanks to the contents of the cloudformation-examples bucket being publicly visible. The latest version of the helper-scripts requires some Python elements/versions that are a pain to set up, but you can use an older version of the helper scripts without any issues. As of CentOS 6.8, you can use aws-cfn-bootstrap-1.4-5.amzn1.noarch.rpm without having to juggle any prerequisites.
CentOS 7
Not as easy. The following code will download the latest helper scripts, unpack them, build them and create symbolic links in the directory in which AWS expects them to exist (/opt/aws/bin).
cd /opt
curl -O https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz
tar -xvpf aws-cfn-bootstrap-latest.tar.gz
cd aws-cfn-bootstrap-1.4/
python setup.py build
python setup.py install
ln -s /usr/init/redhat/cfn-hup /etc/init.d/cfn-hup
chmod 775 /usr/init/redhat/cfn-hup
cd /opt
mkdir aws
cd aws
mkdir bin
ln -s /usr/bin/cfn-hup /opt/aws/bin/cfn-hup
ln -s /usr/bin/cfn-init /opt/aws/bin/cfn-init
ln -s /usr/bin/cfn-signal /opt/aws/bin/cfn-signal
ln -s /usr/bin/cfn-elect-cmd-leader /opt/aws/bin/cfn-elect-cmd-leader
ln -s /usr/bin/cfn-get-metadata /opt/aws/bin/cfn-get-metadata
ln -s /usr/bin/cfn-send-cmd-event /opt/aws/bin/cfn-send-cmd-event
ln -s /usr/bin/cfn-send-cmd-result /opt/aws/bin/cfn-send-cmd-result
RHEL 6
RedHat 6 uses the same version of aws-cfn-bootstrap as CentOS 6, but it gets there in a different way.
rpm -ivh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
yum install -y python-pip
cd /usr/bind
pip install pystache
pip install argparse
pip install python-daemon
pip install requests
yum install -y https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-1.4-5.amzn1.noarch.rpm
First, we have to download epel-release to download python-pip so we can build some dependencies. After installing the dependencies, we download the 1.4-5 version of the scripts.
RHEL 7
The hardest of them all! Or the one that requires the most steps. We have to download an epel-release, install pip, install python packages, download the scripts and create symbolic links.
rpm -ivh http://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-7.noarch.rpm
yum install -y python-pip
cd /usr/bin
pip install pystache
pip install argparse
pip install python-daemon
pip install requests
cd /opt
curl -O https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz
tar -xvpf aws-cfn-bootstrap-latest.tar.gz
cd aws-cfn-bootstrap-1.4/
python setup.py build
python setup.py install
ln -s /usr/init/redhat/cfn-hup /etc/init.d/cfn-hup
chmod 775 /usr/init/redhat/cfn-hup
cd /opt
mkdir aws
cd aws
mkdir bin
ln -s /usr/bin/cfn-hup /opt/aws/bin/cfn-hup
ln -s /usr/bin/cfn-init /opt/aws/bin/cfn-init
ln -s /usr/bin/cfn-signal /opt/aws/bin/cfn-signal
ln -s /usr/bin/cfn-elect-cmd-leader /opt/aws/bin/cfn-elect-cmd-leader
ln -s /usr/bin/cfn-get-metadata /opt/aws/bin/cfn-get-metadata
ln -s /usr/bin/cfn-send-cmd-event /opt/aws/bin/cfn-send-cmd-event
ln -s /usr/bin/cfn-send-cmd-result /opt/aws/bin/cfn-send-cmd-result
Voila! Your custom AMI can now leverage CloudFormation::Init instructions.