2017-10-09 23:04:40 +00:00
|
|
|
# (c) 2016, Bill Wang <ozbillwang(at)gmail.com>
|
2017-10-16 00:31:57 +00:00
|
|
|
# (c) 2017, Marat Bakeev <hawara(at)gmail.com>
|
2018-02-06 22:41:46 +00:00
|
|
|
# (c) 2018, Michael De La Rue <siblemitcom.mddlr(at)spamgourmet.com>
|
2017-10-13 03:52:10 +00:00
|
|
|
# (c) 2017 Ansible Project
|
|
|
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
2017-10-09 23:04:40 +00:00
|
|
|
|
|
|
|
from __future__ import (absolute_import, division, print_function)
|
|
|
|
__metaclass__ = type
|
|
|
|
|
2017-11-10 17:15:19 +00:00
|
|
|
DOCUMENTATION = '''
|
2018-02-06 22:41:46 +00:00
|
|
|
lookup: aws_ssm
|
|
|
|
author:
|
|
|
|
- Bill Wang <ozbillwang(at)gmail.com>
|
|
|
|
- Marat Bakeev <hawara(at)gmail.com>
|
|
|
|
- Michael De La Rue <siblemitcom.mddlr@spamgourmet.com>
|
|
|
|
version_added: 2.5
|
|
|
|
requirements:
|
|
|
|
- boto3
|
|
|
|
- botocore
|
|
|
|
short_description: Get the value for a SSM parameter or all parameters under a path.
|
|
|
|
description:
|
|
|
|
- Get the value for an Amazon Simple Systems Manager parameter or a heirarchy of parameters.
|
|
|
|
The first argument you pass the lookup can either be a parameter name or a hierarchy of
|
|
|
|
parameters. Hierarchies start with a forward slash and end with the parameter name. Up to
|
|
|
|
5 layers may be specified.
|
|
|
|
- When explicitly looking up a parameter by name the parameter being missing will be an error.
|
|
|
|
- When looking up a path for parameters under it a dictionary will be returned for each path.
|
|
|
|
If there is no parameter under that path then the return will be successful but the
|
|
|
|
dictionary will be empty.
|
|
|
|
options:
|
|
|
|
decrypt:
|
|
|
|
description: A boolean to indicate whether to decrypt the parameter.
|
|
|
|
default: false
|
|
|
|
type: boolean
|
|
|
|
bypath:
|
|
|
|
description: A boolean to indicate whether the parameter is provided as a hierarchy.
|
|
|
|
default: false
|
|
|
|
type: boolean
|
|
|
|
recursive:
|
|
|
|
description: A boolean to indicate whether to retrieve all parameters within a hierarchy.
|
|
|
|
default: false
|
|
|
|
type: boolean
|
|
|
|
shortnames:
|
|
|
|
description: Indicates whether to return the name only without path if using a parameter hierarchy.
|
|
|
|
default: false
|
|
|
|
type: boolean
|
2017-11-10 17:15:19 +00:00
|
|
|
'''
|
|
|
|
|
|
|
|
EXAMPLES = '''
|
|
|
|
# lookup sample:
|
|
|
|
- name: lookup ssm parameter store in the current region
|
|
|
|
debug: msg="{{ lookup('aws_ssm', 'Hello' ) }}"
|
|
|
|
|
|
|
|
- name: lookup a key which doesn't exist, returns ""
|
|
|
|
debug: msg="{{ lookup('aws_ssm', 'NoKey') }}"
|
|
|
|
|
|
|
|
- name: lookup ssm parameter store in nominated region
|
2018-02-06 22:41:46 +00:00
|
|
|
debug: msg="{{ lookup('aws_ssm', 'Hello', region=us-east-2 ) }}"
|
2017-11-10 17:15:19 +00:00
|
|
|
|
|
|
|
- name: lookup ssm parameter store without decrypted
|
2018-02-06 22:41:46 +00:00
|
|
|
debug: msg="{{ lookup('aws_ssm', 'Hello', decrypt=False ) }}"
|
2017-11-10 17:15:19 +00:00
|
|
|
|
|
|
|
- name: lookup ssm parameter store in nominated aws profile
|
2018-02-06 22:41:46 +00:00
|
|
|
debug: msg="{{ lookup('aws_ssm', 'Hello', aws_profile=myprofile ) }}"
|
2017-11-10 17:15:19 +00:00
|
|
|
|
|
|
|
- name: lookup ssm parameter store with all options.
|
2018-02-06 22:41:46 +00:00
|
|
|
debug: msg="{{ lookup('aws_ssm', 'Hello', decrypt=false, region=us-east-2, aws_profile=myprofile') }}"
|
2017-11-10 17:15:19 +00:00
|
|
|
|
|
|
|
- name: return a dictionary of ssm parameters from a hierarchy path
|
2018-02-06 22:41:46 +00:00
|
|
|
debug: msg="{{ lookup('aws_ssm', '/PATH/to/params', region=ap-southeast-2, bypath=true, recursive=true' ) }}"
|
2017-11-10 17:15:19 +00:00
|
|
|
|
|
|
|
- name: return a dictionary of ssm parameters from a hierarchy path with shortened names (param instead of /PATH/to/param)
|
2018-02-06 22:41:46 +00:00
|
|
|
debug: msg="{{ lookup('aws_ssm', '/PATH/to/params', region=ap-southeast-2, shortnames=true, bypath=true, recursive=true ) }}"
|
2017-11-10 17:15:19 +00:00
|
|
|
|
|
|
|
- name: Iterate over a parameter hierarchy
|
2018-02-06 22:41:46 +00:00
|
|
|
debug: msg='key contains {{item.Name}} with value {{item.Value}} '
|
|
|
|
loop: '{{ query("aws_ssm", "/TEST/test-list", region="ap-southeast-2", bypath=true) }}'
|
|
|
|
|
2017-11-10 17:15:19 +00:00
|
|
|
'''
|
|
|
|
|
2018-02-06 22:41:46 +00:00
|
|
|
from ansible.module_utils._text import to_native
|
|
|
|
from ansible.module_utils.ec2 import HAS_BOTO3, boto3_tag_list_to_ansible_dict
|
2017-10-09 23:04:40 +00:00
|
|
|
from ansible.errors import AnsibleError
|
|
|
|
from ansible.plugins.lookup import LookupBase
|
2018-02-06 22:41:46 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
from __main__ import display
|
|
|
|
except ImportError:
|
|
|
|
from ansible.utils.display import Display
|
|
|
|
display = Display()
|
2017-10-09 23:04:40 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
from botocore.exceptions import ClientError
|
2018-02-06 22:41:46 +00:00
|
|
|
import botocore
|
2017-10-09 23:04:40 +00:00
|
|
|
import boto3
|
|
|
|
except ImportError:
|
|
|
|
pass # will be captured by imported HAS_BOTO3
|
|
|
|
|
|
|
|
|
2018-02-06 22:41:46 +00:00
|
|
|
def _boto3_conn(region, credentials):
|
|
|
|
if 'boto_profile' in credentials:
|
|
|
|
boto_profile = credentials.pop('boto_profile')
|
|
|
|
else:
|
|
|
|
boto_profile = None
|
|
|
|
|
|
|
|
try:
|
|
|
|
connection = boto3.session.Session(profile_name=boto_profile).client('ssm', region, **credentials)
|
|
|
|
except (botocore.exceptions.ProfileNotFound, botocore.exceptions.PartialCredentialsError):
|
|
|
|
if boto_profile:
|
|
|
|
try:
|
|
|
|
connection = boto3.session.Session(profile_name=boto_profile).client('ssm', region)
|
|
|
|
# FIXME: we should probably do better passing on of the error information
|
|
|
|
except (botocore.exceptions.ProfileNotFound, botocore.exceptions.PartialCredentialsError):
|
|
|
|
raise AnsibleError("Insufficient credentials found.")
|
|
|
|
else:
|
|
|
|
raise AnsibleError("Insufficient credentials found.")
|
|
|
|
return connection
|
|
|
|
|
|
|
|
|
2017-10-09 23:04:40 +00:00
|
|
|
class LookupModule(LookupBase):
|
2018-02-06 22:41:46 +00:00
|
|
|
def run(self, terms, variables=None, boto_profile=None, aws_profile=None,
|
|
|
|
aws_secret_key=None, aws_access_key=None, aws_security_token=None, region=None,
|
|
|
|
bypath=False, shortnames=False, recursive=False, decrypt=True):
|
2017-10-09 23:04:40 +00:00
|
|
|
'''
|
2018-02-06 22:41:46 +00:00
|
|
|
:arg terms: a list of lookups to run.
|
|
|
|
e.g. ['parameter_name', 'parameter_name_too' ]
|
|
|
|
:kwarg variables: ansible variables active at the time of the lookup
|
|
|
|
:kwarg aws_secret_key: identity of the AWS key to use
|
|
|
|
:kwarg aws_access_key: AWS seret key (matching identity)
|
|
|
|
:kwarg aws_security_token: AWS session key if using STS
|
|
|
|
:kwarg decrypt: Set to True to get decrypted parameters
|
|
|
|
:kwarg region: AWS region in which to do the lookup
|
|
|
|
:kwarg bypath: Set to True to do a lookup of variables under a path
|
|
|
|
:kwarg recursive: Set to True to recurse below the path (requires bypath=True)
|
|
|
|
:returns: A list of parameter values or a list of dictionaries if bypath=True.
|
2017-10-09 23:04:40 +00:00
|
|
|
'''
|
|
|
|
|
|
|
|
if not HAS_BOTO3:
|
2018-02-06 22:41:46 +00:00
|
|
|
raise AnsibleError('botocore and boto3 are required for aws_ssm lookup.')
|
2017-10-16 00:31:57 +00:00
|
|
|
|
2018-02-06 22:41:46 +00:00
|
|
|
ret = []
|
|
|
|
response = {}
|
|
|
|
ssm_dict = {}
|
2017-10-09 23:04:40 +00:00
|
|
|
|
2018-02-06 22:41:46 +00:00
|
|
|
credentials = {}
|
|
|
|
if aws_profile:
|
|
|
|
credentials['boto_profile'] = aws_profile
|
2017-10-09 23:04:40 +00:00
|
|
|
else:
|
2018-02-06 22:41:46 +00:00
|
|
|
credentials['boto_profile'] = boto_profile
|
|
|
|
credentials['aws_secret_access_key'] = aws_secret_key
|
|
|
|
credentials['aws_access_key_id'] = aws_access_key
|
|
|
|
credentials['aws_session_token'] = aws_security_token
|
|
|
|
|
|
|
|
client = _boto3_conn(region, credentials)
|
|
|
|
|
|
|
|
ssm_dict['WithDecryption'] = decrypt
|
|
|
|
|
|
|
|
# Lookup by path
|
|
|
|
if bypath:
|
|
|
|
ssm_dict['Recursive'] = recursive
|
|
|
|
for term in terms:
|
|
|
|
ssm_dict["Path"] = term
|
|
|
|
display.vvv("AWS_ssm path lookup term: %s in region: %s" % (term, region))
|
|
|
|
try:
|
|
|
|
response = client.get_parameters_by_path(**ssm_dict)
|
|
|
|
except ClientError as e:
|
|
|
|
raise AnsibleError("SSM lookup exception: {0}".format(to_native(e)))
|
2017-10-16 00:31:57 +00:00
|
|
|
paramlist = list()
|
|
|
|
paramlist.extend(response['Parameters'])
|
|
|
|
|
2018-02-06 22:41:46 +00:00
|
|
|
# Manual pagination, since boto doesn't support it yet for get_parameters_by_path
|
2017-10-16 00:31:57 +00:00
|
|
|
while 'NextToken' in response:
|
|
|
|
response = client.get_parameters_by_path(NextToken=response['NextToken'], **ssm_dict)
|
|
|
|
paramlist.extend(response['Parameters'])
|
|
|
|
|
|
|
|
# shorten parameter names. yes, this will return duplicate names with different values.
|
2018-02-06 22:41:46 +00:00
|
|
|
if shortnames:
|
2017-10-16 00:31:57 +00:00
|
|
|
for x in paramlist:
|
|
|
|
x['Name'] = x['Name'][x['Name'].rfind('/') + 1:]
|
|
|
|
|
2018-02-06 22:41:46 +00:00
|
|
|
display.vvvv("AWS_ssm path lookup returned: %s" % str(paramlist))
|
2017-10-16 00:31:57 +00:00
|
|
|
if len(paramlist):
|
2018-02-06 22:41:46 +00:00
|
|
|
ret.append(boto3_tag_list_to_ansible_dict(paramlist,
|
|
|
|
tag_name_key_name="Name",
|
|
|
|
tag_value_key_name="Value"))
|
2017-10-16 00:31:57 +00:00
|
|
|
else:
|
2018-02-06 22:41:46 +00:00
|
|
|
ret.append({})
|
|
|
|
# Lookup by parameter name - always returns a list with one or no entry.
|
|
|
|
else:
|
|
|
|
display.vvv("AWS_ssm name lookup term: %s" % terms)
|
|
|
|
ssm_dict["Names"] = terms
|
|
|
|
try:
|
2017-10-16 00:31:57 +00:00
|
|
|
response = client.get_parameters(**ssm_dict)
|
2018-02-06 22:41:46 +00:00
|
|
|
except ClientError as e:
|
|
|
|
raise AnsibleError("SSM lookup exception: {0}".format(to_native(e)))
|
|
|
|
if len(response['Parameters']) == len(terms):
|
|
|
|
ret = [p['Value'] for p in response['Parameters']]
|
|
|
|
else:
|
|
|
|
raise AnsibleError('Undefined AWS SSM parameter: %s ' % str(response['InvalidParameters']))
|
2017-10-16 00:31:57 +00:00
|
|
|
|
2018-02-06 22:41:46 +00:00
|
|
|
display.vvvv("AWS_ssm path lookup returning: %s " % str(ret))
|
|
|
|
return ret
|