AWS: CentOS 7 EC2 userdata

Template userdata script for setting up Amazon Machine Images (AMI)

#!/bin/bash

username="centos"
us_east_2_efs_logs="fs-xxxxxxx1"
us_east_2_efs_conf="fs-xxxxxxx2"
us_west_2_efs_logs="fs-xxxxxxx3"
us_west_2_efs_conf="fs-xxxxxxx4"
swap_gb_size=2

yum_update() {
  yum update -y
}

install_eple_and_ius_repo() {
  yum install -y \
    https://repo.ius.io/ius-release-el7.rpm \
    https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
}

install_base_packages() {
  install_eple_and_ius_repo
  yum groupinstall -y 'Development Tools'
  yum install -y wget screen tree vim-enhanced unzip jq git setroubleshoot
  yum install -y htop nmon nc lsof tcpdump mtr bind-utils openssl python36u
}

install_amazon_efs_utils() {
  # Based on: https://docs.aws.amazon.com/efs/latest/ug/using-amazon-efs-utils.html#installing-other-distro
  yum install -y git make rpm-build
  cd /opt/
  git clone https://github.com/aws/efs-utils
  cd efs-utils
  git pull
  make rpm
  yum -y install ./build/amazon-efs-utils*rpm
}

install_awscli() {
  cd /tmp
  curl "https://s3.amazonaws.com/aws-cli/awscli-bundle.zip" -o "awscli-bundle.zip"
  unzip -o awscli-bundle.zip
  ./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws
}

install_aws_ssm_agent() {
  yum install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm
  systemctl disable amazon-ssm-agent
}

install_httpd() {
  cd /etc/yum.repos.d
  wget https://repo.codeit.guru/codeit.el`rpm -q --qf "%{VERSION}" $(rpm -q --whatprovides redhat-release)`.repo
  yum install -y httpd mod_ssl python2-certbot-apache

  config_httpd
  systemctl enable httpd
}

config_httpd() {
  cp -a /etc/httpd/conf/httpd.conf /etc/httpd/conf/httpd.conf.orig
  echo "IncludeOptional sites-enabled/*.conf" >> /etc/httpd/conf/httpd.conf

  mkdir -p /etc/httpd/sites-{available,enabled}
  mv /etc/httpd/conf.d/autoindex.conf /etc/httpd/sites-available/
  mv /etc/httpd/conf.d/userdir.conf /etc/httpd/sites-available/
  mv /etc/httpd/conf.d/welcome.conf /etc/httpd/sites-available/

  config_httpd_selinux

  systemctl restart httpd
}

config_httpd_selinux() {
  setsebool -P httpd_use_nfs 1
  getsebool httpd_use_nfs

  setsebool -P httpd_can_network_connect 1
  getsebool httpd_can_network_connect
}

install_openjdk8() {
  yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-devel
  ln -sf /etc/alternatives/jre_openjdk /usr/lib/jvm/jdk8
}

install_nvm_node_yarn_pm2() {
  runuser -l ${username} -c 'curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.0/install.sh | bash'
  curl --silent --location https://rpm.nodesource.com/setup_12.x | bash -
  curl --silent --location https://dl.yarnpkg.com/rpm/yarn.repo | tee /etc/yum.repos.d/yarn.repo
  yum install -y yarn
  npm install -g pm2
  sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u ${username} --hp /home/${username}
}

set_timezone_to_pacific() {
  timedatectl set-timezone America/Los_Angeles
}

config_bash() {
  echo 'if [ -f /etc/bashrc ]; then
	. /etc/bashrc
fi
PATH=/usr/local/bin:$PATH:$HOME/.local/bin:$HOME/bin
export PATH
' >> /root/.bashrc
  echo 'if [ -f ~/.bash_aliases ]; then
    . ~/.bash_aliases
fi
' | tee -a /root/.bashrc >> /home/${username}/.bashrc

  echo "
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
alias ll='ls -al'
alias la='ls -A'
alias lla='ls -alFA'
alias l='ls -CF'
alias ds='ls -latr'
alias cls='clear'
alias sudo='sudo '
alias vi='vim'
alias v='vim -u NONE -N'
" | tee -a /root/.bash_aliases >> /home/${username}/.bash_aliases
  chown ${username}:${username} /home/${username}/.bash_aliases
}

config_vim() {
  yum install -y git
  runuser -l ${username} -c 'mkdir -p ~/.vim/{autoload,bundle}'
  runuser -l ${username} -c 'curl -LSso ~/.vim/autoload/pathogen.vim https://raw.githubusercontent.com/tpope/vim-pathogen/master/autoload/pathogen.vim'
  runuser -l ${username} -c 'cd ~/.vim/bundle; git clone --depth=1 https://github.com/bling/vim-airline.git'
  runuser -l ${username} -c 'cd ~/.vim/bundle; git clone --depth=1 https://github.com/tpope/vim-repeat.git'
  runuser -l ${username} -c 'cd ~/.vim/bundle; git clone --depth=1 https://github.com/tpope/vim-eunuch.git'
  runuser -l ${username} -c 'cd ~/.vim/bundle; git clone --depth=1 https://github.com/tpope/vim-endwise.git'
  runuser -l ${username} -c 'cd ~/.vim/bundle; git clone --depth=1 https://github.com/tpope/vim-surround.git'
  runuser -l ${username} -c 'cd ~/.vim/bundle; git clone --depth=1 https://github.com/tpope/vim-unimpaired.git'
  runuser -l ${username} -c 'cd ~/.vim/bundle; git clone --depth=1 https://github.com/tpope/vim-commentary.git'
  runuser -l ${username} -c 'cd ~/.vim/bundle; git clone --depth=1 https://github.com/scrooloose/syntastic.git'
  echo '
execute pathogen#infect()
execute pathogen#helptags()
syntax on
filetype plugin indent on
set nocompatible
set history=700
set syntax=on
set autoread
set hlsearch
set showmatch
set cursorline
set scrolloff=999
set ruler
set wildmenu
set laststatus=2
set wildmode=list:longest
set expandtab
set smarttab
set shiftwidth=4
set tabstop=4
set wildmenu
set wildmode=list:longest,list:full
set wildignore+=*/tmp/*,*.swp,*.pyc,*.class
set visualbell
set hidden
set switchbuf=useopen
let mapleader = ","
let g:mapleader = ","
nmap <silent> <leader>d "_d
map <silent> <leader>d "_d
map <leader>/ :setlocal hlsearch!<CR>:set hlsearch?<CR>
map <leader>c :setlocal noignorecase!<CR>:set noignorecase?<CR>
noremap <leader>q :bp <BAR> bd #<CR>
nnoremap <C-h> "ryiw:%s/<C-r>r//gc<Left><Left><Left>
vnoremap <C-h> "ryiw:%s/<C-r>r//gc<Left><Left><Left>
" auto toggle paste
let &t_SI .= "\<Esc>[?2004h"
let &t_EI .= "\<Esc>[?2004l"
inoremap <special> <expr> <Esc>[200~ XTermPasteBegin()
function! XTermPasteBegin()
  set pastetoggle=<Esc>[201~
  set paste
  return ""
endfunction
let g:airline#extensions#branch#enabled = 1
let g:airline#extensions#tabline#enabled = 1
let g:airline#extensions#syntastic#enabled = 1
set statusline+=%#warningmsg#
set statusline+=%{SyntasticStatuslineFlag()}
set statusline+=%*
let g:syntastic_always_populate_loc_list = 1
let g:syntastic_auto_loc_list = 1
let g:syntastic_check_on_open = 1
let g:syntastic_check_on_wq = 0
' >> /home/${username}/.vimrc
  chown ${username}:${username} /home/${username}/.vimrc
  ln -sf /home/${username}/.vimrc /root/.vimrc
  ln -sf /home/${username}/.vim /root/.vim
}

config_screen() {
  echo '
altscreen on
termcapinfo xterm * ti @: te @
defbce on
term xterm-256color
defscrollback 30000
hardstatus alwayslastline "%{= kc}%H (load: %l) %-21<%=%D %Y-%m-%d %02c:%s"
' > /home/${username}/.screenrc
  chown ${username}:${username} /home/${username}/.screenrc
}

config_ulimit() {
  echo "root soft nofile 4096" >> /etc/security/limits.conf
  echo "root hard nofile 10240" >> /etc/security/limits.conf
  echo "${username} soft nofile 4096" >> /etc/security/limits.conf
  echo "${username} hard nofile 10240" >> /etc/security/limits.conf
}

config_chronyd() {
  # CentOS comes with chronyd instead of ntp. See /etc/chrony.conf
  if ! grep -q '^server 169.254.169.123' /etc/chrony.conf 2>/dev/null; then
    sed -i '1s|^|server 169.254.169.123 prefer iburst minpoll 4 maxpoll 4\n|' \
      /etc/chrony.conf
  fi
}

mount_efs_volumes() {
  setsebool -P logrotate_use_nfs 1
  getsebool logrotate_use_nfs

  region=$(curl -s http://169.254.169.254/latest/dynamic/instance-identity/document | jq -r .region)
  mkdir -p /mnt/efs/{logs,conf}

  if [[ ${region} == "us-east-2" ]]; then
    echo "${us_east_2_efs_logs}:/ /mnt/efs/logs efs defaults,_netdev 0 0
${us_east_2_efs_conf}:/ /mnt/efs/conf efs defaults,_netdev 0 0" >> /etc/fstab
    mount -t efs ${us_east_2_efs_logs}:/ /mnt/efs/logs
    mount -t efs ${us_east_2_efs_conf}:/ /mnt/efs/conf
  fi

  if [[ ${region} == "us-west-2" ]]; then
  echo "${us_west_2_efs_logs}:/ /mnt/efs/logs efs defaults,_netdev 0 0
${us_west_2_efs_conf}:/ /mnt/efs/conf efs defaults,_netdev 0 0" >> /etc/fstab
    mount -t efs ${us_west_2_efs_logs}:/ /mnt/efs/logs
    mount -t efs ${us_west_2_efs_conf}:/ /mnt/efs/conf
  fi
}

add_swap_partition() {
    fallocate -l ${swap_gb_size}G /swap.1
    dd if=/dev/zero of=/swap.1 bs=1M count=$((1024 * ${swap_gb_size}))
    chmod 600 /swap.1
    mkswap /swap.1
    swapon /swap.1
    echo "/swap.1 swap swap defaults 0 0" >> /etc/fstab

    echo "vm.swappiness = 10" >> /etc/sysctl.conf
    echo "vm.vfs_cache_pressure = 50" >> /etc/sysctl.conf
    echo "net.core.netdev_max_backlog = 30000" >> /etc/sysctl.conf
    echo "net.ipv4.tcp_max_syn_backlog = 8096" >> /etc/sysctlconf
    echo "net.core.rmem_max = 134217728" >> /etc/sysctl.conf
    echo "net.core.wmem_max = 134217728" >> /etc/sysctl.conf
    echo "net.ipv4.tcp_rmem = 4096 87380 67108864" >> /etc/sysctl.conf
    echo "net.ipv4.tcp_wmem = 4096 87380 67108864" >> /etc/sysctl.conf
}

### main ###

log_file=/var/log/ec2-userdata.log

# All output to one file and to the screen
exec > >(tee ${log_file}) 2>&1

start_time=$(date +%s)
set -x

date "+%F %H:%M:%S %z"
set_timezone_to_pacific
date "+%F %H:%M:%S %z"

yum_update
install_base_packages
install_amazon_efs_utils
install_awscli
install_aws_ssm_agent
install_httpd
install_openjdk8
install_nvm_node_yarn_pm2

config_bash
config_vim
config_screen
config_ulimit
config_chronyd
mount_efs_volumes
add_swap_partition

set +x
end_time=$(date +%s)

echo "$(date "+%F %H:%M:%S %z") Done. Time taken: $((${end_time} - ${start_time})) seconds"

# reboot for timezone, fstab and other changes
reboot

Leave a Comment

Your email address will not be published. Required fields are marked *