amazon-web-servicesamazon-ec2puppetpuppetlabs-aws

How to set hostname in EC2 instance with puppet based bootstrapping


I'm trying to build a puppet managed infrastructure (non-enterprise) in AWS with EC2 instances. Using puppetlabs-aws module I'm able to create the machines by convenient means. Next up is to make local settings on each node, most importantly setting a unique hostname. How can I do this?

One way I know of is to provide a script via the user_data parameter. That would be great, but to be usable I need to be able to parameterize that script in order to avoid duplicating the script once for each agent.

Does it make sense? I'd really appreciate a convenient way of achieving this, as I want to launch new instances programmatically. Any suggestion will be considered.

Update

To give an example of my problem, consider this snippet of my provisioning puppet manifest:

ec2_instance { 'backend':
  ensure => present,
  name => 'backend',
  region => 'us-west-2',
  image_id => 'ami-f0091d91',
  instance_type => 't2.micro',
  key_name => 'mykey',
  security_groups => ['provision-sg'],
  user_data => template('configure.erb'),
}

ec2_instance { 'webfront':
  ensure => present,
  name => 'webfront',
  region => 'us-west-2',
  image_id => 'ami-f0091d91',
  instance_type => 't2.micro',
  key_name => 'mykey',
  security_groups => ['provision-sg'],
  user_data => template('configure.erb'),
}

This will ensure the two instances are up and running. Please notice the user_data => template('configure.erb') referring to a template script which is executed on the instance once it is created. Here I would be able to set the hostname (or whatever I wanted to) if I only knew what data to base the decision on. I can add tags to the instance descriptions, but that is not readable from the configure.erb script as far at I know.

Anyway, setting the hostname is just my idea of solving the root problem. There might be other more convenient methods. What I want is simply a way of having these two instances representing different node types to the puppet master.


Solution

  • The problem is how to set up a new instance with so that it will load it's config from a particular class

    Let me try and explain the problem I think you are trying to address

    What I am trying to answer here

    You have an existing script that sets up EC2 virtual hosts on AWS using the aws-puppet module. This module calls AWS API to actually make EC2 virtual hosts. But they only contain configuration that is "built in" to the AMI file that is used in the API call. A typical AMI file might be a Centos base image. Further configuration is possible at this phase via a "user data script". But let's assume this a shell script, difficult to test and maintain and so not containing complex setup

    So further configuration, install of packages and setup is needed. In order to make this setup happen, there is a second phase of activity from puppet, using entirely different manifests (that are not detailed in the question)

    This second phase is controlled by the new EC2 virtual hosts attaching to the puppet master in their own right. So what I am assuming you are doing is:

    Basic Answer using roles

    Here some ideas of how to make this scenario with two phase configuration of the EC2 hosts work

    At create time make a custom fact "role". Make a file in /etc/facter/facts.d/role.yaml like this

    role: webserver
    

    This can be setup as the instance is made by adding a command like this to a User Data script

    echo 'role: webserver' > /etc/facter/facts.d/role.yaml
    

    As long as this "role" is setup before puppet starts up it will work fine.

    I am assuming that you have a set of modules with manifests and maybe files subdirectories in the module path with the same name as the role

    Next, alter your site.pp to say something like

    include "$role"
    

    And the init.pp from the module will kick in and do the right thing, install packages, configure files etc!

    This idea is explained in more detail here https://puppetlabs.com/presentations/designing-puppet-rolesprofiles-pattern


    Another Approach

    The above is a really crude way of doing it which I haven't tested! Our setup has roles but loads them via hiera configuration. The heira configuration looks somewhat like this

    ---
    :backends:
      - yaml
    :hierarchy:
        - role/%{::role}
        - global
    :yaml:
      :datadir: /etc/puppet/environments/production/hiera
    

    Then I might have a /etc/puppet/environments/production/hiera/role/webserver.yaml file which says

    classes:
      - webserver
      - yum_repos
      - logstash
      - java8
    

    And the end of the site.pp says

    hiera_include('classes')
    

    Which loads all the relevant "classes" definitions from the modules_include files

    This has the advantage that multiple classes can be loaded by each role with much less duplication of code

    The "global" part of the yaml configuration is intended for classes that are loaded by everything in your environment, for example admin user ssh keys


    defined type example

    Here is an example of how you might use a defined type as a wrapper around ec2_instance to pass the "myrole" into the template. I have not tested this, I don't have the aws puppet stuff installed

    define my_instance( 
      $ensure = present,
      $region = 'us-west-2',
      $image_id = 'ami-f0091d91',
      $instance_type = 't2.micro',
      $key_name= 'mykey',
      $security_groups = ['provision-sg'],
      $myrole = 'webserver'
      )
    {
    ec2_instance { $title :
      ensure => $ensure,
      name => $title,
      region => $region,
      image_id => $image_id,
      instance_type => $instance_type,
      key_name => $key,
      security_groups => $security_groups,
      user_data => template('configure.erb'),
    }
    }
    
    $instance_data={
      'backend' =>
      {
      ensure => present,
      name => 'backend',
      region => 'us-west-2',
      image_id => 'ami-f0091d91',
      instance_type => 't2.micro',
      key_name => 'mykey',
      security_groups => ['provision-sg'],
      myrole => 'voodooswamp'
    },
      'webfront'=>
      {
      ensure => present,
      region => 'us-west-2',
      image_id => 'ami-f0091d91',
      instance_type => 't2.micro',
      key_name => 'mykey',
      security_groups => ['provision-sg'],
      myrole => 'humanfly'
      }
    }
    
    
    create_resources(my_instance, $instance_data)