Skip to content

Instantly share code, notes, and snippets.

@philfreo
Last active March 26, 2020 03:42
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save philfreo/9ab42cae3f7e930f01882491b3c4e0d9 to your computer and use it in GitHub Desktop.
Save philfreo/9ab42cae3f7e930f01882491b3c4e0d9 to your computer and use it in GitHub Desktop.
AWS User script to bootstrap any instance
#!/bin/bash -x
# User script to bootstrap any instance.
# This will set up, array, and format every instance storage
# drive present in the instance (if any)
#
# The disk format/layout is read from an EC2 tag "DiskLayout"
# (defined in the cloudformation templates) and it defaults to
# "raid0". Possible values are:
# * raidN: grabs all available instance storage drives, creates a
# single software raid device with RAID level N, mounts it under
# /mnt and moves /usr, /var, /home to it (the EBS root volumes are
# usually tiny)
# * jbod: grabs each available instance storage drives and mounts
# it under separate subdirectories under /mnt. Currently unused
# but it might come in handy
# Upgrade packages
DEBIAN_FRONTEND=noninteractive apt-get -y update;
DEBIAN_FRONTEND=noninteractive apt-get -y -f upgrade;
# Force date adjustment and restart cron
service ntp stop
ntpdate -s 0.ubuntu.pool.ntp.org
service ntp start
service cron restart
# will need the raid tools and the aws tools
DEBIAN_FRONTEND=noninteractive apt-get install -y mdadm awscli;
# We'll ask the metadata api for the block device mapping
# http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html
function curly() {
local uri="$1";
curl -s "http://169.254.169.254/latest/meta-data/${uri}" && echo;
}
function get_tag() {
local tag="$1";
local default="${2:-}"
# Get this instance's information from metadata api
local instance_id="$( curly instance-id )";
local az="$( curly placement/availability-zone )";
# cut the last char from the AZ and you get the Region
local region="${az%?}";
# thanks to the magic of amazon tags
# http://docs.aws.amazon.com/cli/latest/reference/ec2/describe-tags.html
local value=`aws --region=${region} ec2 describe-tags --output text --filters "Name=resource-id,Values=${instance_id}" | grep ${tag} | awk '{ print $NF }'`;
if [ -z "$value" ]; then
echo $default;
else
echo $value;
fi;
}
function move_dirs() {
# Since EC2 instance-store backed instances have only 10GB root
# partitions, we want to mount important root directories to
# a different (larger) mount point
# See:
# http://www.turnkeylinux.org/docs/using-instance-storage
local target="$1";
for dir in var usr home; do
mv /${dir} ${target}/;
mkdir -p /${dir};
echo -e "${target}/${dir}\t/${dir}\tbind\tbind\t0 0" >> /etc/fstab
mount /${dir};
done;
}
# Set the hostname. These instructions are copied/adjusted from fabfile_puppet
hostname="$( get_tag Name localhost )";
if [ "$hostname" == "localhost" ]; then
domain="localdomain";
else
domain="example.com";
fi;
fqdn="${hostname}.${domain}";
hostname $hostname;
echo "$hostname" > /etc/hostname;
echo "# This file is automatically generated by our bootstrap script.
127.0.0.1 localhost.localdomain localhost
127.0.0.1 ${fqdn} ${hostname}
# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts" > /etc/hosts;
# These instances come with an entry in fstab for xvdb
# we want to remove this line so we can add the correct entries
# and unmount it for later manipulation
sed -i '/^\/dev\/xvdb/d' /etc/fstab;
umount /dev/xvdb;
disk_layout="$( get_tag DiskLayout raid0 )";
# our storage arrays
eph_drives=();
trim_drives=();
# Get all block devices as per Amazon's API and stick them in the array
for drive in $( curly block-device-mapping/ | grep ^ephemeral | paste -s -d' ' ); do
# Get the actual device name translated to linux lingo i.e. sdb -> xvdb
dev="$( curly block-device-mapping/${drive} | sed -e 's/s/xv/' )";
eph_drives[${#eph_drives[*]}]="/dev/${dev}";
# Does this device support TRIM? By rule amazon doesn't mix TRIM/non-TRIM
# drives, so if you have at least one of them they are all TRIM
# http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/InstanceStorage.html#instance-store-volumes
if lsblk -n -l -o "NAME,DISC-MAX" | grep ^${dev} | grep -v '0B'; then
trim_drives[${#trim_drives[*]}]="/dev/${dev}";
fi;
done;
# The fstab options change if there are TRIM capable drives
# http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ssd-instance-store.html#InstanceStoreTrimSupport
if [ -z "${trim_drives[*]}" ]; then
fstab_opt="defaults,nobootwait";
else
fstab_opt="defaults,discard";
fi;
# building a software RAID array given a tag (or default)
if [[ ! -z "${eph_drives[*]}" && "$disk_layout" == "raid"* ]]; then
level="$( echo $disk_layout | sed -e 's/raid//' )";
if [ ${#eph_drives[*]} -gt 1 ]; then
device="/dev/md/eph:0";
# create a software raid device and configure so it rebuilds properly every boot
# this will force the array even if the drives haven't been partitioned or have
# an existing file system
mdadm --create $device --run --force --level=${level} --raid-devices=${#eph_drives[*]} ${eph_drives[*]};
mdadm --detail --scan --verbose | sed -e "s/metadata=.*\ //" > /etc/mdadm/mdadm.conf;
else
# single drive
device="${eph_drives[0]}";
fi;
# EXT4 all the things
mkfs.ext4 -E nodiscard -F $device;
# mount it properly
mkdir -p /mnt;
echo -e "${device}\t/mnt\text4\t${fstab_opt}\t0 0" >> /etc/fstab
mount /mnt;
move_dirs /mnt;
fi;
# JBOD-style configuration
if [[ ! -z "${eph_drives[*]}" && "$disk_layout" == "jbod" ]]; then
first=true;
for device in ${eph_drives[*]}; do
name="$( basename $device )";
mkfs.ext4 -E nodiscard -F $device;
mkdir -p /mnt/${name};
echo -e "${device}\t/mnt/${name}\text4\t${fstab_opt}\t0 0" >> /etc/fstab
mount /mnt/${name};
# move our stuff to the first device
if $first; then
move_dirs /mnt/${name};
first=false;
fi;
done;
fi;
# Setup the puppet agent
puppetmaster="$( get_tag PuppetMaster puppet )"
DEBIAN_FRONTEND=noninteractive apt-get install -y puppet;
echo "# This file is automatically generated by our bootstrap script.
[main]
logdir=/var/log/puppet
vardir=/var/lib/puppet
ssldir=/var/lib/puppet/ssl
rundir=/var/run/puppet
factpath=\$vardir/lib/facter
templatedir=\$confdir/templates
parser = future
[agent]
server=${puppetmaster}
report = true
classfile = \$vardir/classes.txt
localconfig = \$vardir/localconfig
graph = true
" > /etc/puppet/puppet.conf;
puppet agent --enable;
puppet agent --no-daemonize --onetime --verbose --no-splay --parser=future --logdest="/var/log/puppet/firstrun.log";
service puppet restart;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment