Skip to content

Instantly share code, notes, and snippets.

@dmuntean
Forked from hemebond/awspsv.py
Created September 19, 2018 11:22
Show Gist options
  • Save dmuntean/f784398be33e5bc10aa43da35f05315b to your computer and use it in GitHub Desktop.
Save dmuntean/f784398be33e5bc10aa43da35f05315b to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
# 2018-02-15 Updated to be compatible with Salt-Cloud 2017.7.3
import sys
import os.path
import yaml
import boto3
import salt.config
import salt.wheel
import salt.cloud
from salt.exceptions import SaltInvocationError
import tempfile
from salt.utils.templates import render_jinja_tmpl
def autosign(name, output=True):
'''
Create a file in minions_autosign to pre-approve a minion
'''
ret = {}
autosign_key = os.path.join(__opts__['pki_dir'], 'minions_autosign', name)
open(autosign_key, 'a').close()
ret['key'] = autosign_key
return ret
def profile(profile_name, name):
'''
Pass is a profile to create a new EC2 instance with the name provided.
profile is the name of the Salt Cloud profile to use in creating the EC2 instance.
name is the name of the instance
'''
#
# Read in the Salt Master and Salt Cloud configurations
#
ret = {}
opts = salt.config.cloud_config('/etc/salt/cloud')
cloud = salt.cloud.CloudClient('/etc/salt/cloud')
wheel = salt.wheel.WheelClient(opts)
try:
profile = opts['profiles'][profile_name]
except KeyError:
raise SaltInvocationError('Profile %s not found' % profile)
provider_name, provider_type = profile.get('provider').split(':')
try:
provider = opts['providers'][provider_name][provider_type]
except KeyError:
raise SaltInvocationError('Provider %s not found' % profile['provider'])
minion_id = name
# This dict should match what Cloud passes around internally
vm_ = {
'provider': provider_name,
'profile': profile_name,
'driver': provider_name,
'name': name
}
# Deploy must be set to False to prevent conflict
if salt.config.get_cloud_config_value('deploy', vm_, opts) == True:
raise SaltCloudConfigError('Deploy can not be true')
# Empty dict to hold configuration for create_instances
cfg = {}
instance_cfg = {}
# userdata_file
cfg['userdata_file'] = salt.config.get_cloud_config_value('userdata_file', vm_, opts)
if cfg['userdata_file']:
profile_fields = [
'provider',
'image',
'size',
'ssh_username',
'volumes',
'tag',
'sync_after_install',
'keyname',
'securitygroup',
'iam_profile',
'ssh_interface',
'ssh_gateway',
'ssh_gateway_port',
'ssh_gateway_username',
'ssh_gateway_private_key',
'ssh_gateway_password',
'location',
'availability_zone',
'tenancy',
'subnetid',
'subnetname',
'securitygroupid',
'securitygroupname',
'placementgroup',
'spot_config',
'block_device_mappings',
'min_instance',
'max_instance',
'userdata_file',
'userdata',
'network_interfaces',
'ebs_optimized',
'del_root_vol_on_destroy',
]
# Create a temporary file to hold the result of the userdata_file template render
userdata_tmp = tempfile.NamedTemporaryFile(delete=False)
# image
cfg['image'] = salt.config.get_cloud_config_value('image', vm_, opts)
if cfg['image']:
instance_cfg['ImageId'] = cfg['image']
# keyname
cfg['keyname'] = salt.config.get_cloud_config_value('keyname', vm_, opts)
if cfg['keyname']:
instance_cfg['KeyName'] = cfg['keyname']
# size
cfg['size'] = salt.config.get_cloud_config_value('size', vm_, opts)
if cfg['size']:
instance_cfg['InstanceType'] = cfg['size']
# minion
cfg['minion'] = salt.config.get_cloud_config_value('minion', vm_, opts)
# iam_profile
cfg['iam_profile'] = salt.config.get_cloud_config_value('iam_profile', vm_, opts)
if cfg['iam_profile']:
instance_cfg['IamInstanceProfile'] = {'Arn': cfg['iam_profile']}
# Render the cloud-init template via Jinja
init_cxt = {
'opts': opts,
'saltenv': None,
'minion_id': minion_id,
'profile': cfg
}
# Read in the cloud-init template
init_tpl = open(cfg['userdata_file']).read()
# Render the template
init_rdr = render_jinja_tmpl(init_tpl, init_cxt)
instance_cfg['UserData'] = init_rdr
# Save the result to a temporary file
userdata_tmp.write(init_rdr + '\n')
userdata_tmp.close()
#
# If we're given a minion ID (to manually create an instance) then
# generate the minion key and user data now
#
aws_access_id = salt.config.get_cloud_config_value('id', vm_, opts)
aws_secret_key = salt.config.get_cloud_config_value('key', vm_, opts)
if not (aws_access_id and aws_secret_key):
raise SaltInvocationError('Need S3 credentials')
s3_bucket = salt.config.get_cloud_config_value('s3_bucket', vm_, opts)
s3_path = salt.config.get_cloud_config_value('s3_path', vm_, opts)
if s3_bucket and s3_path:
keys = wheel.cmd('key.gen_accept', [minion_id])
# If the key has already been accepted `keys` will be an empty dict
if keys == {}:
print('Minion key for %s already exists. No key will be created' % minion_id)
else:
# Upload the new keys to S3
s3 = boto3.client('s3', aws_access_key_id=aws_access_id,
aws_secret_access_key=aws_secret_key)
s3.put_object(Key='%s/%s.pem' % (s3_path, minion_id), Bucket=s3_bucket, Body=keys['priv'])
s3.put_object(Key='%s/%s.pub' % (s3_path, minion_id), Bucket=s3_bucket, Body=keys['pub'])
# Create a new instance in EC2
vm_overrides = {}
if 'userdata_file' in cfg and cfg['userdata_file']:
# Override the userdata_file parameter to point to our rendered temporary file
vm_overrides['userdata_file'] = userdata_tmp.name
instance = cloud.profile(profile_name, [minion_id,], vm_overrides=vm_overrides)
ret['instance'] = instance
ret['keys'] = {
'priv': 's3://%s/%s/%s.pem' % (s3_bucket, s3_path, minion_id),
'pub': 's3://%s/%s/%s.pub' % (s3_bucket, s3_path, minion_id),
}
return ret
else:
raise SaltInvocationError('No S3 bucket and path provided.')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment