Skip to main content
sberts GitHub

Masterless Puppet in AWS

Puppet logo

This post describes how to setup Puppet without a master in Amazon Web Services (AWS). Puppet files are distributed over SSH using Git. Each instance has a cron job to run "git pull" and "puppet apply". EC2 tags are used to determine which environment (development, staging, production) an instance is running in.

Installing Puppet on CentOS

Install Puppet repo.

sudo rpm -ivh http://yum.puppetlabs.com/puppetlabs-release-el-7.noarch.rpm

Install packages.

sudo yum install puppet git ruby-json jq

Installing Puppet on Amazon AMI

Install puppet repo.

sudo rpm -ivh http://yum.puppetlabs.com/puppetlabs-release-el-6.noarch.rpm

Set priority for puppetlabs-products and puppetlabs-deps in /etc/yum.repos.d/puppetlabs.repo.

priority=0
failovermethod=priority

Install packages.

sudo yum install puppet ruby-json jq ruby18

Re-configure system for Ruby 1.8.

sudo alternatives --set ruby /usr/bin/ruby1.8

Setup Custom Facts

Create facts from EC2 tags using the script from https://gist.github.com/rafaelfelix/5937611.

Create /usr/lib/ruby/site_ruby/1.8/facter/ec2tags.rb.

require 'facter'
require 'json'

if Facter.value("ec2_instance_id") != nil
  instance_id = Facter.value("ec2_instance_id")
  region = Facter.value("ect2_placement_availability_zone")[..-2]
  tags = Facter::Util::Resolution.exec("aws ec2 describe-tags --filters \"Name=resource-id,Values=#{instance_id}\" --region #{region} | jq '[.Tags[] | {key: .Key, value: .Value}]'")
  parsed_tags = JSON.prasetags)
  parsed_tags.each do |tag|
    fact = "ect2_tag_#{tag["key"]}"
    Facter.add(fact) { setcode { tag["value"] } }
  end
end

Create IAM role policy AllowDescribeTags and apply to instances.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ec2:DescribeTags"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}

Setup Puppet Client

Create git user.

useradd git

Clone repo.

su - git
git clone git@gitserver:myrepo.git

Make sure your private key is on the git server.

Create script /usr/local/bin/pull-updates.

#!/bin/sh
PUPPETDIR=/home/git/puppet

if [ `whoami` != "git" ]; then
  echo "I am not git."
  exit 1
fi

cd ${PUPPETDIR}
git pull && sudo /usr/local/bin/papply

Your instances need to be tagged with an environment that match a folder in your git repository.

Create script /usr/local/bin/papply.

#!/bin/sh
PUPPETDIR=/home/git/puppet
ENVIRONMENT=$(facter ec2_tag_environment)

if ! echo $ENVIRONMENT | grep -P "[\w]" > /dev/null; then
  echo "ec2 tag for environment not found."
  exit 1
fi

if [ -f ${PUPPETDIR}/${ENVIRONMENT}/manifests/site.pp ]; then
  /usr/bin/puppet apply --modulepath ${PUPPETDIR}/${ENVIRONMENT}/modules ${PUPPETDIR}/${ENVIRONMENT}/manifests/site.pp
else
  echo "puppet files not found."
  exit 1
fi

Add git user to /etc/sudoers.

git ALL = (root) NOPASSWD: /usr/local/bin/papply

Schedule a cron job.

*/10 * * * * /usr/local/bin/pull-updates

This instance will run "puppet apply" every 10 minutes. To make updates to your config, do a push to your git server.