No, this is not a post about Guitar Hero 3. It is about system administration and puppetry!
If you manage more than 5 hosts you
will need configuration management automation. Either you write your own scripts (shudder), use CFEngine (more shudder) or you use
Puppet from Reductive labs (There are .deb packages: puppet and puppetmaster).
Choose a server that is going to be the puppetmaster. This guy should be fairly well protected as he will be the server handing out configuration to other machines. Give him a repository in /etc/puppet via either Subversion, Git, Hg, Bzr or your poison of preference. Make it such that a DNS lookup on puppet.[dnsdomainname] hits the puppetmaster. Enable the puppetmaster fileserver if not already done.
Install puppet on the clients. Hand it the server address and it will try to communicate. It will fail miserably until you allow the clients Security CA's on the puppetmaster. Then you are ready for the fun: Assimilation!
I started with something easy. Behold, sudo.pp:
class sudo {
package { sudo: ensure => installed }
file { "/etc/sudoers":
owner => root,
group => root,
mode => 440,
source => "puppet://puppet/dist/apps/sudo/sudoers",
require => Package[sudo]
}
}
This incantation tells puppet that we need the 'sudo' package. Puppet is intelligent enough to use the right poison: portage, apt, aptitude, yum, pkg-install (Hi FreeBSD!), etc for installing the package. Next it will update /etc/sudoers with a file over the puppet protocol from the 'puppet' host (Remember the DNS setup? You'll love it in the long run ;) and take it from /dist/apps/sudo/sudoers. This path is prefixed with whatever is set in /etc/puppet/fileserver.conf and the file is copied, checksummed and configured. B00m! You now have centralized your sudo configuration.
Next up: NTP!
class openntpd {
package { openntpd: ensure => installed }
file { "/etc/openntpd/ntpd.conf":
source => "puppet://puppet/dist/apps/openntpd/ntpd.conf",
require => Package[openntpd]
}
service { openntpd:
ensure => running,
enable => true,
pattern => "ntpd",
subscribe => [Package[openntpd], File["/etc/openntpd/ntpd.conf"]]
}
}
Note we have a declaration 'require' that orders the dependencies among things. We'll be sure things trigger in the right order. Next we define that openntpd needs a service 'openntpd' which should be running and enabled. The 'pattern' part tells puppet that it should look for a program 'ntpd' to discern whether openntpd is running or not.
Puppet is intelligent enough to force the enabling via 'update-rc.d' on my Ubuntu/Debian boxes.
Final thing: Locales (shudder).
class locales {
package { locales: ensure => installed }
file {
"/etc/environment":
owner => root,
group => root,
mode => 644,
source => "puppet://puppet/dist/config/environment",
require => Package[locales];
"/etc/locale.gen":
owner => root,
group => root,
mode => 644,
source => "puppet://puppet/dist/config/locale.gen",
require => Package[locales],
before => Exec["generate locales"];
}
exec { "locale_gen":
command => "/usr/sbin/locale-gen",
alias => "generate locales",
subscribe => File["/etc/locale.gen"],
refreshonly => true
}
}
The new thing is the 'exec' declaration that will call '/usr/sbin/locale-gen' if our subscription '/etc/locale.gen' were updated (refreshed).
Now you add the classes to the machines that need them through the 'node' facility of puppet. And then you have centralized control over ntpd and locales as well.
Of course, puppet can also do things depending on host-types, fill in config-file templates, discriminate configuration in every conceivable way and so on. I can live with the fact it is written in Ruby. It happens to be brilliant.
If you want to start using it, I recommend going straight to the root of a
complete setup, and use that as a base for yours.