Skip to content

Commit

Permalink
Work to allow security tokens and profiles to work with Ansible
Browse files Browse the repository at this point in the history
Allow security tokens and profiles to be used as arguments
to the 'common' ec2 modules

Mostly refactoring to provide two new methods,
`get_aws_connection_info`, which results in a dict that can be
passed through to the boto `connect_to_region` calls, and
`connect_to_aws` that can pass that dict through to the
`connect_to_region` method of the appropriate module.

Tidied up some variable names

Works around boto/boto#2100

profiles don't work with boto < 2.24, but this detects for that
and fails with an appropriate message. It is designed to work
if profile is not passed but boto < 2.24 is installed.

Modifications to allow empty aws auth variables to be passed
(this is useful if wanting to have the keys as an optional
parameter in ec2 calls - if set, use this value, if not set,
use boto config or env variables)

Reworked validate_certs improvements to work with refactoring

Added documentation for profile and security_token to affected modules
  • Loading branch information
willthames committed Mar 6, 2014
1 parent 372ae07 commit b9a7352
Show file tree
Hide file tree
Showing 9 changed files with 198 additions and 32 deletions.
113 changes: 83 additions & 30 deletions lib/ansible/module_utils/ec2.py
Expand Up @@ -14,55 +14,72 @@
'us-west-2']


def ec2_argument_keys_spec():
def aws_common_argument_spec():
return dict(
ec2_url=dict(),
aws_secret_key=dict(aliases=['ec2_secret_key', 'secret_key'], no_log=True),
aws_access_key=dict(aliases=['ec2_access_key', 'access_key']),
validate_certs=dict(default=True, type='bool'),
security_token=dict(no_log=True),
profile=dict(),
)
return spec


def ec2_argument_spec():
spec = ec2_argument_keys_spec()
spec = aws_common_argument_spec()
spec.update(
dict(
region=dict(aliases=['aws_region', 'ec2_region'], choices=AWS_REGIONS),
validate_certs=dict(default=True, type='bool'),
ec2_url=dict(),
)
)
return spec


def get_ec2_creds(module):
def boto_supports_profile_name():
return hasattr(boto.ec2.EC2Connection, 'profile_name')


def get_aws_connection_info(module):

# Check module args for credentials, then check environment vars
# access_key

ec2_url = module.params.get('ec2_url')
ec2_secret_key = module.params.get('aws_secret_key')
ec2_access_key = module.params.get('aws_access_key')
access_key = module.params.get('aws_access_key')
secret_key = module.params.get('aws_secret_key')
security_token = module.params.get('security_token')
region = module.params.get('region')
profile_name = module.params.get('profile')
validate_certs = module.params.get('validate_certs')

if not ec2_url:
if 'EC2_URL' in os.environ:
ec2_url = os.environ['EC2_URL']
elif 'AWS_URL' in os.environ:
ec2_url = os.environ['AWS_URL']

if not ec2_access_key:
if not access_key:
if 'EC2_ACCESS_KEY' in os.environ:
ec2_access_key = os.environ['EC2_ACCESS_KEY']
access_key = os.environ['EC2_ACCESS_KEY']
elif 'AWS_ACCESS_KEY_ID' in os.environ:
ec2_access_key = os.environ['AWS_ACCESS_KEY_ID']
access_key = os.environ['AWS_ACCESS_KEY_ID']
elif 'AWS_ACCESS_KEY' in os.environ:
ec2_access_key = os.environ['AWS_ACCESS_KEY']
access_key = os.environ['AWS_ACCESS_KEY']
else:
# in case access_key came in as empty string
access_key = None

if not ec2_secret_key:
if not secret_key:
if 'EC2_SECRET_KEY' in os.environ:
ec2_secret_key = os.environ['EC2_SECRET_KEY']
secret_key = os.environ['EC2_SECRET_KEY']
elif 'AWS_SECRET_ACCESS_KEY' in os.environ:
ec2_secret_key = os.environ['AWS_SECRET_ACCESS_KEY']
secret_key = os.environ['AWS_SECRET_ACCESS_KEY']
elif 'AWS_SECRET_KEY' in os.environ:
ec2_secret_key = os.environ['AWS_SECRET_KEY']
secret_key = os.environ['AWS_SECRET_KEY']
else:
# in case secret_key came in as empty string
secret_key = None

if not region:
if 'EC2_REGION' in os.environ:
Expand All @@ -71,39 +88,75 @@ def get_ec2_creds(module):
region = os.environ['AWS_REGION']
else:
# boto.config.get returns None if config not found
region = boto.config.get('Boto', 'aws_region')
region = boto.config.get('Boto', 'aws_region')
if not region:
region = boto.config.get('Boto', 'ec2_region')

return ec2_url, ec2_access_key, ec2_secret_key, region
if not security_token:
if 'AWS_SECURITY_TOKEN' in os.environ:
security_token = os.environ['AWS_SECURITY_TOKEN']
else:
# in case security_token came in as empty string
security_token = None

boto_params = dict(aws_access_key_id=access_key,
aws_secret_access_key=secret_key,
security_token=security_token)

# profile_name only works as a key in boto >= 2.24
# so only set profile_name if passed as an argument
if profile_name:
if not boto_supports_profile_name():
module.fail_json("boto does not support profile_name before 2.24")
boto_params['profile_name'] = profile_name

if validate_certs and HAS_LOOSE_VERSION and LooseVersion(boto.Version) >= LooseVersion("2.6.0"):
boto_params['validate_certs'] = validate_certs

return region, ec2_url, boto_params


def get_ec2_creds(module):
''' for compatibility mode with old modules that don't/can't yet
use ec2_connect method '''
region, ec2_url, boto_params = get_aws_connection_info(module)
return ec2_url, boto_params['aws_access_key_id'], boto_params['aws_secret_access_key'], region


def boto_fix_security_token_in_profile(conn, profile_name):
''' monkey patch for boto issue boto/boto#2100 '''
profile = 'profile ' + profile_name
if boto.config.has_option(profile, 'aws_security_token'):
conn.provider.set_security_token(boto.config.get(profile, 'aws_security_token'))
return conn


def connect_to_aws(aws_module, region, **params):
conn = aws_module.connect_to_region(region, **params)
if params.get('profile_name'):
conn = boto_fix_security_token_in_profile(conn, params['profile_name'])
return conn


def ec2_connect(module):

""" Return an ec2 connection"""

ec2_url, aws_access_key, aws_secret_key, region = get_ec2_creds(module)
validate_certs = module.params.get('validate_certs', True)
region, ec2_url, boto_params = get_aws_connection_info(module)

# If we have a region specified, connect to its endpoint.
if region:
try:
if HAS_LOOSE_VERSION and LooseVersion(boto.Version) >= LooseVersion("2.6.0"):
ec2 = boto.ec2.connect_to_region(region, aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_key, validate_certs=validate_certs)
else:
ec2 = boto.ec2.connect_to_region(region, aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_key)
ec2 = connect_to_aws(boto.ec2, region, **boto_params)
except boto.exception.NoAuthHandlerFound, e:
module.fail_json(msg = str(e))
module.fail_json(msg=str(e))
# Otherwise, no region so we fallback to the old connection method
elif ec2_url:
try:
if HAS_LOOSE_VERSION and LooseVersion(boto.Version) >= LooseVersion("2.6.0"):
ec2 = boto.connect_ec2_endpoint(ec2_url, aws_access_key, aws_secret_key, validate_certs=validate_certs)
else:
ec2 = boto.connect_ec2_endpoint(ec2_url, aws_access_key, aws_secret_key)
ec2 = boto.connect_ec2_endpoint(ec2_url, **boto_params)
except boto.exception.NoAuthHandlerFound, e:
module.fail_json(msg = str(e))
module.fail_json(msg=str(e))
else:
module.fail_json(msg="Either region or ec2_url must be specified")
return ec2

return ec2
14 changes: 14 additions & 0 deletions library/cloud/ec2
Expand Up @@ -220,6 +220,20 @@ options:
choices: ["yes", "no"]
aliases: []
version_added: "1.5"
profile:
description:
- uses a boto profile. Only works with boto >= 2.24.0
required: false
default: null
aliases: []
version_added: "1.5"
security_token:
description:
- security token to authenticate against AWS
required: false
default: null
aliases: []
version_added: "1.5"
requirements: [ "boto" ]
author: Seth Vidal, Tim Gerla, Lester Wade
Expand Down
14 changes: 14 additions & 0 deletions library/cloud/ec2_ami
Expand Up @@ -109,6 +109,20 @@ options:
choices: ["yes", "no"]
aliases: []
version_added: "1.5"
profile:
description:
- uses a boto profile. Only works with boto >= 2.24.0
required: false
default: null
aliases: []
version_added: "1.5"
security_token:
description:
- security token to authenticate against AWS
required: false
default: null
aliases: []
version_added: "1.5"
requirements: [ "boto" ]
author: Evan Duffield <eduffield@iacquire.com>
Expand Down
14 changes: 14 additions & 0 deletions library/cloud/ec2_eip
Expand Up @@ -61,6 +61,20 @@ options:
choices: ["yes", "no"]
aliases: []
version_added: "1.5"
profile:
description:
- uses a boto profile. Only works with boto >= 2.24.0
required: false
default: null
aliases: []
version_added: "1.5"
security_token:
description:
- security token to authenticate against AWS
required: false
default: null
aliases: []
version_added: "1.5"
requirements: [ "boto" ]
author: Lorin Hochstein <lorin@nimbisservices.com>
Expand Down
14 changes: 14 additions & 0 deletions library/cloud/ec2_group
Expand Up @@ -65,6 +65,20 @@ options:
choices: ["yes", "no"]
aliases: []
version_added: "1.5"
profile:
description:
- uses a boto profile. Only works with boto >= 2.24.0
required: false
default: null
aliases: []
version_added: "1.5"
security_token:
description:
- security token to authenticate against AWS
required: false
default: null
aliases: []
version_added: "1.5"
requirements: [ "boto" ]
'''
Expand Down
14 changes: 14 additions & 0 deletions library/cloud/ec2_key
Expand Up @@ -56,6 +56,20 @@ options:
choices: ["yes", "no"]
aliases: []
version_added: "1.5"
profile:
description:
- uses a boto profile. Only works with boto >= 2.24.0
required: false
default: null
aliases: []
version_added: "1.5"
security_token:
description:
- security token to authenticate against AWS
required: false
default: null
aliases: []
version_added: "1.5"
requirements: [ "boto" ]
author: Vincent Viallet
Expand Down
19 changes: 17 additions & 2 deletions library/cloud/ec2_snapshot
Expand Up @@ -59,17 +59,32 @@ options:
default: null
aliases: []
instance_id:
description:
description:
- instance that has a the required volume to snapshot mounted
required: false
default: null
aliases: []
device_name:
description:
description:
- device name of a mounted volume to be snapshotted
required: false
default: null
aliases: []
profile:
description:
- uses a boto profile. Only works with boto >= 2.24.0
required: false
default: null
aliases: []
version_added: "1.5"
security_token:
description:
- security token to authenticate against AWS
required: false
default: null
aliases: []
version_added: "1.5"
requirements: [ "boto" ]
author: Will Thames
'''
Expand Down
14 changes: 14 additions & 0 deletions library/cloud/ec2_tag
Expand Up @@ -67,6 +67,20 @@ options:
choices: ["yes", "no"]
aliases: []
version_added: "1.5"
profile:
description:
- uses a boto profile. Only works with boto >= 2.24.0
required: false
default: null
aliases: []
version_added: "1.5"
security_token:
description:
- security token to authenticate against AWS
required: false
default: null
aliases: []
version_added: "1.5"
requirements: [ "boto" ]
author: Lester Wade
Expand Down
14 changes: 14 additions & 0 deletions library/cloud/ec2_vol
Expand Up @@ -90,6 +90,20 @@ options:
choices: ["yes", "no"]
aliases: []
version_added: "1.5"
profile:
description:
- uses a boto profile. Only works with boto >= 2.24.0
required: false
default: null
aliases: []
version_added: "1.5"
security_token:
description:
- security token to authenticate against AWS
required: false
default: null
aliases: []
version_added: "1.5"
requirements: [ "boto" ]
author: Lester Wade
Expand Down

0 comments on commit b9a7352

Please sign in to comment.