diff --git a/lib/ansible/module_utils/ec2.py b/lib/ansible/module_utils/ec2.py index 58291c2d5d..98f9da92d4 100644 --- a/lib/ansible/module_utils/ec2.py +++ b/lib/ansible/module_utils/ec2.py @@ -42,33 +42,44 @@ AWS_REGIONS = ['ap-northeast-1', '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: @@ -76,21 +87,27 @@ def get_ec2_creds(module): 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: @@ -99,39 +116,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 diff --git a/library/cloud/ec2 b/library/cloud/ec2 index f9d1a42813..23ec3eabff 100644 --- a/library/cloud/ec2 +++ b/library/cloud/ec2 @@ -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.6" + security_token: + description: + - security token to authenticate against AWS + required: false + default: null + aliases: [] + version_added: "1.6" requirements: [ "boto" ] author: Seth Vidal, Tim Gerla, Lester Wade diff --git a/library/cloud/ec2_ami b/library/cloud/ec2_ami index 866f2caf76..446c7417e0 100644 --- a/library/cloud/ec2_ami +++ b/library/cloud/ec2_ami @@ -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.6" + security_token: + description: + - security token to authenticate against AWS + required: false + default: null + aliases: [] + version_added: "1.6" requirements: [ "boto" ] author: Evan Duffield diff --git a/library/cloud/ec2_eip b/library/cloud/ec2_eip index de041f4222..7eac979841 100644 --- a/library/cloud/ec2_eip +++ b/library/cloud/ec2_eip @@ -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.6" + security_token: + description: + - security token to authenticate against AWS + required: false + default: null + aliases: [] + version_added: "1.6" requirements: [ "boto" ] author: Lorin Hochstein diff --git a/library/cloud/ec2_group b/library/cloud/ec2_group index bbbb0fc24e..1dd463cc8d 100644 --- a/library/cloud/ec2_group +++ b/library/cloud/ec2_group @@ -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.6" + security_token: + description: + - security token to authenticate against AWS + required: false + default: null + aliases: [] + version_added: "1.6" requirements: [ "boto" ] ''' diff --git a/library/cloud/ec2_key b/library/cloud/ec2_key index 5e6950d2c8..289deb6c9d 100644 --- a/library/cloud/ec2_key +++ b/library/cloud/ec2_key @@ -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.6" + security_token: + description: + - security token to authenticate against AWS + required: false + default: null + aliases: [] + version_added: "1.6" requirements: [ "boto" ] author: Vincent Viallet diff --git a/library/cloud/ec2_snapshot b/library/cloud/ec2_snapshot index b5d9df3b52..e637ebefa3 100644 --- a/library/cloud/ec2_snapshot +++ b/library/cloud/ec2_snapshot @@ -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.6" + security_token: + description: + - security token to authenticate against AWS + required: false + default: null + aliases: [] + version_added: "1.6" + requirements: [ "boto" ] author: Will Thames ''' diff --git a/library/cloud/ec2_tag b/library/cloud/ec2_tag index c96a8be6f7..92af644933 100644 --- a/library/cloud/ec2_tag +++ b/library/cloud/ec2_tag @@ -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.6" + security_token: + description: + - security token to authenticate against AWS + required: false + default: null + aliases: [] + version_added: "1.6" requirements: [ "boto" ] author: Lester Wade diff --git a/library/cloud/ec2_vol b/library/cloud/ec2_vol index bdd2eae382..faacc82da8 100644 --- a/library/cloud/ec2_vol +++ b/library/cloud/ec2_vol @@ -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.6" + security_token: + description: + - security token to authenticate against AWS + required: false + default: null + aliases: [] + version_added: "1.6" requirements: [ "boto" ] author: Lester Wade