Using Salt-Cloud in AWS EC2

While the official documentation on using Salt-Cloud to provision AWS EC2 is fairly complete, it’s likely useful to walk through the process here.

First you’ll need to ensure that the salt-cloud command is available on your system:

\# For CentOS
yum install -y salt-cloud

# For Mac OS X - http://docs.saltstack.com/en/latest/topics/installation/osx.html
sudo pip install salt

Next you’ll need to configure your cloud provider:

/etc/salt/cloud.providers.d/aws.provider

profile-us-west-2-public-ips:
  # Set up the location of the salt master
  minion:
    master: saltmaster.example.com

  # Specify whether to use public or private IP for deploy script.
  # Valid options are:
  #     private\_ips - The salt-cloud command is run inside the EC2
  #     public\_ips - The salt-cloud command is run outside of EC2
  ssh\_interface: public\_ips

  # Set the EC2 access credentials - prefer IAM, see below.
  id: 'use-instance-role-credentials'
  key: 'use-instance-role-credentials'

  # Make sure this key is owned by root with permissions 0400.
  private\_key: /etc/salt/my\_test\_key.pem
  keyname: my\_test\_key
  securitygroup: default

  # Optionally configure default region
  location: us-west-2
  availability\_zone: us-west-2c

  # Configure which user to use to run the deploy script.
  ssh\_username: ec2-user

  # Optionally add an IAM profile <-- preferred to credentials
  iam\_profile: 'arn:aws:iam::123456789012:instance-profile/ExampleInstanceProfile'

  provider: ec2

Next you’ll need to set up a profile at /etc/salt/cloud.profiles:

base\_ec2:
  provider: profile-us-west-2-public-ips
  image: ami-873rhd9
  size: t2.medium
  ssh\_username: ec2-user
  # Optional: add tags to profile:
  tag: {'Environment': 'production', 'Role': 'Generic'}
  # Optional: upload and run a script
  script:  /etc/salt/cloud.deploy.d/user\_data.sh
  # Optional: define networking components
  network\_interfaces:
    - DeviceIndex: 0
      PrivateIpAddresses:
        - Primary: True
      #auto assign public ip (not EIP)
      AssociatePublicIpAddress: True
      SecurityGroupId:
        - sg-750af413

With a cloud profile and a provider set up you can now start creating instances

  • salt-cloud -p [cloud profile] [desired minion-id]!

For official SaltStack documentation on this topic, check out the latest documentation on salt’s implementation of AWS EC2.

Salt-Cloud was developed at the same time as the team was working on the Reactor system - and while I will probably do a separate post on Salt’s Reactor system, it’s probably good to do a short overview here as well.

At a high level all events in Salt-Cloud have a tag, which generally includes the instance-id and a description of the task. A typical Salt-Cloud tag is similar to: salt/cloud/i-2893472/creating or salt/cloud/i-2893472/created. The available events typically include:

  1. salt/cloud/[id]/creating:
    This event confirms that the instance has started creating. The payload in this event contains “name”, “profile”, and “provider”.

  2. salt/cloud/[id]/requesting:
    Salt-Cloud makes a request to the cloud provider to create an instance. The payload of this event will reflect the variables included in the request to spawn the instance. The payload in this event generally contains “name”, “image”, “size”, and “location”. This can be dependant on the provider. Experiment!

  3. salt/cloud/[id]/querying:
    The instance has been requested, but not all information to log into the instance is available. The payload in this event normally only contains the “instance-id”.

  4. salt/cloud/[id]/waiting_for_ssh:
    The information required to log into the instance has been retrieved, but the instance is not necessarily ready to be accessed. Following this event, Salt-Cloud will wait for the IP address to respond to a ping, then wait for the specified port (usually 22) to respond to a connection and for SSH to become available. Salt-Cloud will attempt to issue the date command on the remote system, as a means to check for availability. If no ssh_username has been specified, a list of usernames (starting with root) will be attempted. If one or more usernames was configured for ssh_username, they will be added to the beginning of the list, in order. The payload for this event normally only contains the “ip”.

  5. salt/cloud/[id]/deploying:
    SSH has been successful and Salt-Cloud has begun uploading any files used for deployment, and will run the deploy script. Once the script has completed, Salt_Cloud will run a cleanup. The payload of this stage will contain a number of variables that were used to deploy the instances. Note: Sensitive data is usually scraped from the payload.

  6. salt/cloud/[id]/created:
    The instance is now fully bootstrapped, and ready for use. This is generally the last event for Salt-Cloud and returns the instance information to the user. The payload in this event generally contains “name”, “profile”, and “provider”.

Here’s an example of what a payload looks like:

{ 'name': 'i-2893472', 'profile': 'ec2-centos', 'provider': 'ec2-config' }

On your salt-master you would create a file at /etc/salt/master.d/reactor. You can shard this out to several files if needed, but generally just one is fine.

A reactor file is typically structured like:

reactor:
  - 'event/path/argument/1':
    - '/srv/reactor/standard.sls'
  - 'event/path/argument/2':
    - '/srv/reactor/standard.sls'
  - 'event/path/argument/3':
    - '/srv/reactor/standard.sls'

A more practical example:

/etc/salt/master.d/reactor

reactor:
  - 'salt/minion/\*/start':
    - '/srv/reactor/custom-reactor.sls'
  - 'salt/cloud/\*/created':
    - '/srv/reactor/cloud-alert.sls'
  - 'salt/cloud/\*/destroyed':
    - '/srv/reactor/cloud-destroy-alert.sls'

Where “*” represents an instance-id or similar.

The sls files called by the reactor use the same format as any other state file. An example from the official documentation illustrates how you can fire a pagerduty alert, but I like the example that calls a highstate when the provisioning is complete:

\# /etc/salt/master.d/reactor
reactor:
  - 'salt/cloud/\*/created':
    - '/srv/reactor/startup\_highstate.sls'

\# /srv/reactor/startup\_highstate.sls
reactor\_highstate:
  cmd.state.highstate:
    - tgt: {{ data\['name'\] }}

I highly recommend taking a look at the official documentation, available here.