Init: mediaserver

This commit is contained in:
2023-02-08 12:13:28 +01:00
parent 848bc9739c
commit f7c23d4ba9
31914 changed files with 6175775 additions and 0 deletions

View File

@@ -0,0 +1,136 @@
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = '''
name: aws_account_attribute
author:
- Sloane Hertel (@s-hertel) <shertel@redhat.com>
extends_documentation_fragment:
- amazon.aws.boto3
- amazon.aws.aws_credentials
- amazon.aws.aws_region
short_description: Look up AWS account attributes
description:
- Describes attributes of your AWS account. You can specify one of the listed
attribute choices or omit it to see all attributes.
options:
attribute:
description: The attribute for which to get the value(s).
choices:
- supported-platforms
- default-vpc
- max-instances
- vpc-max-security-groups-per-interface
- max-elastic-ips
- vpc-max-elastic-ips
- has-ec2-classic
'''
EXAMPLES = """
vars:
has_ec2_classic: "{{ lookup('aws_account_attribute', attribute='has-ec2-classic') }}"
# true | false
default_vpc_id: "{{ lookup('aws_account_attribute', attribute='default-vpc') }}"
# vpc-xxxxxxxx | none
account_details: "{{ lookup('aws_account_attribute', wantlist='true') }}"
# {'default-vpc': ['vpc-xxxxxxxx'], 'max-elastic-ips': ['5'], 'max-instances': ['20'],
# 'supported-platforms': ['VPC', 'EC2'], 'vpc-max-elastic-ips': ['5'], 'vpc-max-security-groups-per-interface': ['5']}
"""
RETURN = """
_raw:
description:
Returns a boolean when I(attribute) is check_ec2_classic. Otherwise returns the value(s) of the attribute
(or all attributes if one is not specified).
"""
try:
import boto3
import botocore
except ImportError:
pass # will be captured by imported HAS_BOTO3
from ansible.errors import AnsibleError
from ansible.module_utils._text import to_native
from ansible.module_utils.basic import missing_required_lib
from ansible.plugins.lookup import LookupBase
from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry
from ansible_collections.amazon.aws.plugins.module_utils.ec2 import HAS_BOTO3
def _boto3_conn(region, credentials):
boto_profile = credentials.pop('aws_profile', None)
try:
connection = boto3.session.Session(profile_name=boto_profile).client('ec2', region, **credentials)
except (botocore.exceptions.ProfileNotFound, botocore.exceptions.PartialCredentialsError):
if boto_profile:
try:
connection = boto3.session.Session(profile_name=boto_profile).client('ec2', region)
except (botocore.exceptions.ProfileNotFound, botocore.exceptions.PartialCredentialsError):
raise AnsibleError("Insufficient credentials found.")
else:
raise AnsibleError("Insufficient credentials found.")
return connection
def _get_credentials(options):
credentials = {}
credentials['aws_profile'] = options['aws_profile']
credentials['aws_secret_access_key'] = options['aws_secret_key']
credentials['aws_access_key_id'] = options['aws_access_key']
if options['aws_security_token']:
credentials['aws_session_token'] = options['aws_security_token']
return credentials
@AWSRetry.jittered_backoff(retries=10)
def _describe_account_attributes(client, **params):
return client.describe_account_attributes(**params)
class LookupModule(LookupBase):
def run(self, terms, variables, **kwargs):
if not HAS_BOTO3:
raise AnsibleError(missing_required_lib('botocore and boto3'))
self.set_options(var_options=variables, direct=kwargs)
boto_credentials = _get_credentials(self._options)
region = self._options['region']
client = _boto3_conn(region, boto_credentials)
attribute = kwargs.get('attribute')
params = {'AttributeNames': []}
check_ec2_classic = False
if 'has-ec2-classic' == attribute:
check_ec2_classic = True
params['AttributeNames'] = ['supported-platforms']
elif attribute:
params['AttributeNames'] = [attribute]
try:
response = _describe_account_attributes(client, **params)['AccountAttributes']
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
raise AnsibleError("Failed to describe account attributes: %s" % to_native(e))
if check_ec2_classic:
attr = response[0]
return any(value['AttributeValue'] == 'EC2' for value in attr['AttributeValues'])
if attribute:
attr = response[0]
return [value['AttributeValue'] for value in attr['AttributeValues']]
flattened = {}
for k_v_dict in response:
flattened[k_v_dict['AttributeName']] = [value['AttributeValue'] for value in k_v_dict['AttributeValues']]
return flattened

View File

@@ -0,0 +1,295 @@
# Copyright: (c) 2018, Aaron Smith <ajsmith10381@gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r'''
name: aws_secret
author:
- Aaron Smith (!UNKNOWN) <ajsmith10381@gmail.com>
extends_documentation_fragment:
- amazon.aws.boto3
- amazon.aws.aws_credentials
- amazon.aws.aws_region
short_description: Look up secrets stored in AWS Secrets Manager
description:
- Look up secrets stored in AWS Secrets Manager provided the caller
has the appropriate permissions to read the secret.
- Lookup is based on the secret's I(Name) value.
- Optional parameters can be passed into this lookup; I(version_id) and I(version_stage)
options:
_terms:
description: Name of the secret to look up in AWS Secrets Manager.
required: True
bypath:
description: A boolean to indicate whether the parameter is provided as a hierarchy.
default: false
type: boolean
version_added: 1.4.0
nested:
description: A boolean to indicate the secret contains nested values.
type: boolean
default: false
version_added: 1.4.0
version_id:
description: Version of the secret(s).
required: False
version_stage:
description: Stage of the secret version.
required: False
join:
description:
- Join two or more entries to form an extended secret.
- This is useful for overcoming the 4096 character limit imposed by AWS.
- No effect when used with I(bypath).
type: boolean
default: false
on_deleted:
description:
- Action to take if the secret has been marked for deletion.
- C(error) will raise a fatal error when the secret has been marked for deletion.
- C(skip) will silently ignore the deleted secret.
- C(warn) will skip over the deleted secret but issue a warning.
default: error
type: string
choices: ['error', 'skip', 'warn']
version_added: 2.0.0
on_missing:
description:
- Action to take if the secret is missing.
- C(error) will raise a fatal error when the secret is missing.
- C(skip) will silently ignore the missing secret.
- C(warn) will skip over the missing secret but issue a warning.
default: error
type: string
choices: ['error', 'skip', 'warn']
on_denied:
description:
- Action to take if access to the secret is denied.
- C(error) will raise a fatal error when access to the secret is denied.
- C(skip) will silently ignore the denied secret.
- C(warn) will skip over the denied secret but issue a warning.
default: error
type: string
choices: ['error', 'skip', 'warn']
'''
EXAMPLES = r"""
- name: lookup secretsmanager secret in the current region
debug: msg="{{ lookup('amazon.aws.aws_secret', '/path/to/secrets', bypath=true) }}"
- name: Create RDS instance with aws_secret lookup for password param
rds:
command: create
instance_name: app-db
db_engine: MySQL
size: 10
instance_type: db.m1.small
username: dbadmin
password: "{{ lookup('amazon.aws.aws_secret', 'DbSecret') }}"
tags:
Environment: staging
- name: skip if secret does not exist
debug: msg="{{ lookup('amazon.aws.aws_secret', 'secret-not-exist', on_missing='skip')}}"
- name: warn if access to the secret is denied
debug: msg="{{ lookup('amazon.aws.aws_secret', 'secret-denied', on_denied='warn')}}"
- name: lookup secretsmanager secret in the current region using the nested feature
debug: msg="{{ lookup('amazon.aws.aws_secret', 'secrets.environments.production.password', nested=true) }}"
# The secret can be queried using the following syntax: `aws_secret_object_name.key1.key2.key3`.
# If an object is of the form `{"key1":{"key2":{"key3":1}}}` the query would return the value `1`.
- name: lookup secretsmanager secret in a specific region using specified region and aws profile using nested feature
debug: >
msg="{{ lookup('amazon.aws.aws_secret', 'secrets.environments.production.password', region=region, aws_profile=aws_profile,
aws_access_key=aws_access_key, aws_secret_key=aws_secret_key, nested=true) }}"
# The secret can be queried using the following syntax: `aws_secret_object_name.key1.key2.key3`.
# If an object is of the form `{"key1":{"key2":{"key3":1}}}` the query would return the value `1`.
# Region is the AWS region where the AWS secret is stored.
# AWS_profile is the aws profile to use, that has access to the AWS secret.
"""
RETURN = r"""
_raw:
description:
Returns the value of the secret stored in AWS Secrets Manager.
"""
import json
try:
import boto3
import botocore
except ImportError:
pass # will be captured by imported HAS_BOTO3
from ansible.errors import AnsibleError
from ansible.module_utils.six import string_types
from ansible.module_utils._text import to_native
from ansible.module_utils.basic import missing_required_lib
from ansible.plugins.lookup import LookupBase
from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code
from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_message
from ansible_collections.amazon.aws.plugins.module_utils.ec2 import HAS_BOTO3
def _boto3_conn(region, credentials):
boto_profile = credentials.pop('aws_profile', None)
try:
connection = boto3.session.Session(profile_name=boto_profile).client('secretsmanager', region, **credentials)
except (botocore.exceptions.ProfileNotFound, botocore.exceptions.PartialCredentialsError):
if boto_profile:
try:
connection = boto3.session.Session(profile_name=boto_profile).client('secretsmanager', region)
except (botocore.exceptions.ProfileNotFound, botocore.exceptions.PartialCredentialsError):
raise AnsibleError("Insufficient credentials found.")
else:
raise AnsibleError("Insufficient credentials found.")
return connection
class LookupModule(LookupBase):
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, nested=False, join=False, version_stage=None, version_id=None, on_missing='error',
on_denied='error', on_deleted='error'):
'''
: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 secret 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 nested: Set to True to do a lookup of nested secrets
:kwarg join: Join two or more entries to form an extended secret
:kwarg version_stage: Stage of the secret version
:kwarg version_id: Version of the secret(s)
:kwarg on_missing: Action to take if the secret is missing
:kwarg on_deleted: Action to take if the secret is marked for deletion
:kwarg on_denied: Action to take if access to the secret is denied
:returns: A list of parameter values or a list of dictionaries if bypath=True.
'''
if not HAS_BOTO3:
raise AnsibleError(missing_required_lib('botocore and boto3'))
deleted = on_deleted.lower()
if not isinstance(deleted, string_types) or deleted not in ['error', 'warn', 'skip']:
raise AnsibleError('"on_deleted" must be a string and one of "error", "warn" or "skip", not %s' % deleted)
missing = on_missing.lower()
if not isinstance(missing, string_types) or missing not in ['error', 'warn', 'skip']:
raise AnsibleError('"on_missing" must be a string and one of "error", "warn" or "skip", not %s' % missing)
denied = on_denied.lower()
if not isinstance(denied, string_types) or denied not in ['error', 'warn', 'skip']:
raise AnsibleError('"on_denied" must be a string and one of "error", "warn" or "skip", not %s' % denied)
credentials = {}
if aws_profile:
credentials['aws_profile'] = aws_profile
else:
credentials['aws_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
# fallback to IAM role credentials
if not credentials['aws_profile'] and not (
credentials['aws_access_key_id'] and credentials['aws_secret_access_key']):
session = botocore.session.get_session()
if session.get_credentials() is not None:
credentials['aws_access_key_id'] = session.get_credentials().access_key
credentials['aws_secret_access_key'] = session.get_credentials().secret_key
credentials['aws_session_token'] = session.get_credentials().token
client = _boto3_conn(region, credentials)
if bypath:
secrets = {}
for term in terms:
try:
paginator = client.get_paginator('list_secrets')
paginator_response = paginator.paginate(
Filters=[{'Key': 'name', 'Values': [term]}])
for object in paginator_response:
if 'SecretList' in object:
for secret_obj in object['SecretList']:
secrets.update({secret_obj['Name']: self.get_secret_value(
secret_obj['Name'], client, on_missing=missing, on_denied=denied)})
secrets = [secrets]
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
raise AnsibleError("Failed to retrieve secret: %s" % to_native(e))
else:
secrets = []
for term in terms:
value = self.get_secret_value(term, client,
version_stage=version_stage, version_id=version_id,
on_missing=missing, on_denied=denied, on_deleted=deleted,
nested=nested)
if value:
secrets.append(value)
if join:
joined_secret = []
joined_secret.append(''.join(secrets))
return joined_secret
return secrets
def get_secret_value(self, term, client, version_stage=None, version_id=None, on_missing=None, on_denied=None, on_deleted=None, nested=False):
params = {}
params['SecretId'] = term
if version_id:
params['VersionId'] = version_id
if version_stage:
params['VersionStage'] = version_stage
if nested:
if len(term.split('.')) < 2:
raise AnsibleError("Nested query must use the following syntax: `aws_secret_name.<key_name>.<key_name>")
secret_name = term.split('.')[0]
params['SecretId'] = secret_name
try:
response = client.get_secret_value(**params)
if 'SecretBinary' in response:
return response['SecretBinary']
if 'SecretString' in response:
if nested:
query = term.split('.')[1:]
secret_string = json.loads(response['SecretString'])
ret_val = secret_string
for key in query:
if key in ret_val:
ret_val = ret_val[key]
else:
raise AnsibleError("Successfully retrieved secret but there exists no key {0} in the secret".format(key))
return str(ret_val)
else:
return response['SecretString']
except is_boto3_error_message('marked for deletion'):
if on_deleted == 'error':
raise AnsibleError("Failed to find secret %s (marked for deletion)" % term)
elif on_deleted == 'warn':
self._display.warning('Skipping, did not find secret (marked for deletion) %s' % term)
except is_boto3_error_code('ResourceNotFoundException'): # pylint: disable=duplicate-except
if on_missing == 'error':
raise AnsibleError("Failed to find secret %s (ResourceNotFound)" % term)
elif on_missing == 'warn':
self._display.warning('Skipping, did not find secret %s' % term)
except is_boto3_error_code('AccessDeniedException'): # pylint: disable=duplicate-except
if on_denied == 'error':
raise AnsibleError("Failed to access secret %s (AccessDenied)" % term)
elif on_denied == 'warn':
self._display.warning('Skipping, access denied for secret %s' % term)
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except
raise AnsibleError("Failed to retrieve secret: %s" % to_native(e))
return None

View File

@@ -0,0 +1,90 @@
# (c) 2016 James Turner <turnerjsm@gmail.com>
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = '''
name: aws_service_ip_ranges
author:
- James Turner (!UNKNOWN) <turnerjsm@gmail.com>
requirements:
- must have public internet connectivity
short_description: Look up the IP ranges for services provided in AWS such as EC2 and S3.
description:
- AWS publishes IP ranges used on the public internet by EC2, S3, CloudFront, CodeBuild, Route53, and Route53 Health Checking.
- This module produces a list of all the ranges (by default) or can narrow down the list to the specified region or service.
options:
service:
description: 'The service to filter ranges by. Options: EC2, S3, CLOUDFRONT, CODEbUILD, ROUTE53, ROUTE53_HEALTHCHECKS'
region:
description: 'The AWS region to narrow the ranges to. Examples: us-east-1, eu-west-2, ap-southeast-1'
ipv6_prefixes:
description: 'When I(ipv6_prefixes=True) the lookup will return ipv6 addresses instead of ipv4 addresses'
version_added: 2.1.0
'''
EXAMPLES = """
vars:
ec2_ranges: "{{ lookup('aws_service_ip_ranges', region='ap-southeast-2', service='EC2', wantlist=True) }}"
tasks:
- name: "use list return option and iterate as a loop"
debug: msg="{% for cidr in ec2_ranges %}{{ cidr }} {% endfor %}"
# "52.62.0.0/15 52.64.0.0/17 52.64.128.0/17 52.65.0.0/16 52.95.241.0/24 52.95.255.16/28 54.66.0.0/16 "
- name: "Pull S3 IP ranges, and print the default return style"
debug: msg="{{ lookup('aws_service_ip_ranges', region='us-east-1', service='S3') }}"
# "52.92.16.0/20,52.216.0.0/15,54.231.0.0/17"
"""
RETURN = """
_raw:
description: comma-separated list of CIDR ranges
"""
import json
from ansible.errors import AnsibleError
from ansible.module_utils.six.moves.urllib.error import HTTPError
from ansible.module_utils.six.moves.urllib.error import URLError
from ansible.module_utils._text import to_native
from ansible.module_utils.urls import ConnectionError
from ansible.module_utils.urls import open_url
from ansible.module_utils.urls import SSLValidationError
from ansible.plugins.lookup import LookupBase
class LookupModule(LookupBase):
def run(self, terms, variables, **kwargs):
if "ipv6_prefixes" in kwargs and kwargs["ipv6_prefixes"]:
prefixes_label = "ipv6_prefixes"
ip_prefix_label = "ipv6_prefix"
else:
prefixes_label = "prefixes"
ip_prefix_label = "ip_prefix"
try:
resp = open_url('https://ip-ranges.amazonaws.com/ip-ranges.json')
amazon_response = json.load(resp)[prefixes_label]
except getattr(json.decoder, 'JSONDecodeError', ValueError) as e:
# on Python 3+, json.decoder.JSONDecodeError is raised for bad
# JSON. On 2.x it's a ValueError
raise AnsibleError("Could not decode AWS IP ranges: %s" % to_native(e))
except HTTPError as e:
raise AnsibleError("Received HTTP error while pulling IP ranges: %s" % to_native(e))
except SSLValidationError as e:
raise AnsibleError("Error validating the server's certificate for: %s" % to_native(e))
except URLError as e:
raise AnsibleError("Failed look up IP range service: %s" % to_native(e))
except ConnectionError as e:
raise AnsibleError("Error connecting to IP range service: %s" % to_native(e))
if 'region' in kwargs:
region = kwargs['region']
amazon_response = (item for item in amazon_response if item['region'] == region)
if 'service' in kwargs:
service = str.upper(kwargs['service'])
amazon_response = (item for item in amazon_response if item['service'] == service)
iprange = [item[ip_prefix_label] for item in amazon_response]
return iprange

View File

@@ -0,0 +1,286 @@
# (c) 2016, Bill Wang <ozbillwang(at)gmail.com>
# (c) 2017, Marat Bakeev <hawara(at)gmail.com>
# (c) 2018, Michael De La Rue <siblemitcom.mddlr(at)spamgourmet.com>
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = '''
name: aws_ssm
author:
- Bill Wang (!UNKNOWN) <ozbillwang(at)gmail.com>
- Marat Bakeev (!UNKNOWN) <hawara(at)gmail.com>
- Michael De La Rue (!UNKNOWN) <siblemitcom.mddlr@spamgourmet.com>
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 hierarchy 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.
- If looking up an explicitly listed parameter by name which does not exist then the lookup
will generate an error. You can use the ```default``` filter to give a default value in
this case but must set the ```on_missing``` parameter to ```skip``` or ```warn```. You must
also set the second parameter of the ```default``` filter to ```true``` (see examples below).
- 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 lookup will generate an error.
- If the lookup fails due to lack of permissions or due to an AWS client error then the aws_ssm
will generate an error. If you want to continue in this case then you will have to set up
two ansible tasks, one which sets a variable and ignores failures and one which uses the value
of that variable with a default. See the examples below.
options:
decrypt:
description: A boolean to indicate whether to decrypt the parameter.
default: true
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
on_missing:
description:
- Action to take if the SSM parameter is missing.
- C(error) will raise a fatal error when the SSM parameter is missing.
- C(skip) will silently ignore the missing SSM parameter.
- C(warn) will skip over the missing SSM parameter but issue a warning.
default: error
type: string
choices: ['error', 'skip', 'warn']
version_added: 2.0.0
on_denied:
description:
- Action to take if access to the SSM parameter is denied.
- C(error) will raise a fatal error when access to the SSM parameter is denied.
- C(skip) will silently ignore the denied SSM parameter.
- C(warn) will skip over the denied SSM parameter but issue a warning.
default: error
type: string
choices: ['error', 'skip', 'warn']
version_added: 2.0.0
endpoint:
description: Use a custom endpoint when connecting to SSM service.
type: string
version_added: 3.3.0
extends_documentation_fragment:
- amazon.aws.boto3
'''
EXAMPLES = '''
# lookup sample:
- name: lookup ssm parameter store in the current region
debug: msg="{{ lookup('aws_ssm', 'Hello' ) }}"
- name: lookup ssm parameter store in specified region
debug: msg="{{ lookup('aws_ssm', 'Hello', region='us-east-2' ) }}"
- name: lookup ssm parameter store without decryption
debug: msg="{{ lookup('aws_ssm', 'Hello', decrypt=False ) }}"
- name: lookup ssm parameter store using a specified aws profile
debug: msg="{{ lookup('aws_ssm', 'Hello', aws_profile='myprofile' ) }}"
- name: lookup ssm parameter store using explicit aws credentials
debug: msg="{{ lookup('aws_ssm', 'Hello', aws_access_key=my_aws_access_key, aws_secret_key=my_aws_secret_key, aws_security_token=my_security_token ) }}"
- name: lookup ssm parameter store with all options
debug: msg="{{ lookup('aws_ssm', 'Hello', decrypt=false, region='us-east-2', aws_profile='myprofile') }}"
- name: lookup ssm parameter and fail if missing
debug: msg="{{ lookup('aws_ssm', 'missing-parameter') }}"
- name: lookup a key which doesn't exist, returning a default ('root')
debug: msg="{{ lookup('aws_ssm', 'AdminID', on_missing="skip") | default('root', true) }}"
- name: lookup a key which doesn't exist failing to store it in a fact
set_fact:
temp_secret: "{{ lookup('aws_ssm', '/NoAccess/hiddensecret') }}"
ignore_errors: true
- name: show fact default to "access failed" if we don't have access
debug: msg="{{ 'the secret was:' ~ temp_secret | default('could not access secret') }}"
- name: return a dictionary of ssm parameters from a hierarchy path
debug: msg="{{ lookup('aws_ssm', '/PATH/to/params', region='ap-southeast-2', bypath=true, recursive=true ) }}"
- name: return a dictionary of ssm parameters from a hierarchy path with shortened names (param instead of /PATH/to/param)
debug: msg="{{ lookup('aws_ssm', '/PATH/to/params', region='ap-southeast-2', shortnames=true, bypath=true, recursive=true ) }}"
- name: Iterate over a parameter hierarchy (one iteration per parameter)
debug: msg='Key contains {{ item.key }} , with value {{ item.value }}'
loop: '{{ lookup("aws_ssm", "/demo/", region="ap-southeast-2", bypath=True) | dict2items }}'
- name: Iterate over multiple paths as dictionaries (one iteration per path)
debug: msg='Path contains {{ item }}'
loop: '{{ lookup("aws_ssm", "/demo/", "/demo1/", bypath=True)}}'
- name: lookup ssm parameter warn if access is denied
debug: msg="{{ lookup('aws_ssm', 'missing-parameter', on_denied="warn" ) }}"
'''
try:
import botocore
except ImportError:
pass # will be captured by imported HAS_BOTO3
from ansible.errors import AnsibleError
from ansible.module_utils._text import to_native
from ansible.plugins.lookup import LookupBase
from ansible.utils.display import Display
from ansible.module_utils.six import string_types
from ansible.module_utils.basic import missing_required_lib
from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_conn
from ansible_collections.amazon.aws.plugins.module_utils.ec2 import get_aws_connection_info
from ansible_collections.amazon.aws.plugins.module_utils.ec2 import HAS_BOTO3
from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_tag_list_to_ansible_dict
from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code
display = Display()
class LookupModule(LookupBase):
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, on_missing="error",
on_denied="error", endpoint=None):
'''
: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 secret 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)
:kwarg on_missing: Action to take if the SSM parameter is missing
:kwarg on_denied: Action to take if access to the SSM parameter is denied
:kwarg endpoint: Endpoint for SSM client
:returns: A list of parameter values or a list of dictionaries if bypath=True.
'''
if not HAS_BOTO3:
raise AnsibleError(missing_required_lib('botocore and boto3'))
# validate arguments 'on_missing' and 'on_denied'
if on_missing is not None and (not isinstance(on_missing, string_types) or on_missing.lower() not in ['error', 'warn', 'skip']):
raise AnsibleError('"on_missing" must be a string and one of "error", "warn" or "skip", not %s' % on_missing)
if on_denied is not None and (not isinstance(on_denied, string_types) or on_denied.lower() not in ['error', 'warn', 'skip']):
raise AnsibleError('"on_denied" must be a string and one of "error", "warn" or "skip", not %s' % on_denied)
ret = []
ssm_dict = {}
self.params = variables
cli_region, cli_endpoint, cli_boto_params = get_aws_connection_info(self, boto3=True)
if region:
cli_region = region
if endpoint:
cli_endpoint = endpoint
# For backward compatibility
if aws_access_key:
cli_boto_params.update({'aws_access_key_id': aws_access_key})
if aws_secret_key:
cli_boto_params.update({'aws_secret_access_key': aws_secret_key})
if aws_security_token:
cli_boto_params.update({'aws_session_token': aws_security_token})
if boto_profile:
cli_boto_params.update({'profile_name': boto_profile})
if aws_profile:
cli_boto_params.update({'profile_name': aws_profile})
cli_boto_params.update(dict(
conn_type='client',
resource='ssm',
region=cli_region,
endpoint=cli_endpoint,
))
client = boto3_conn(module=self, **cli_boto_params)
ssm_dict['WithDecryption'] = decrypt
# Lookup by path
if bypath:
ssm_dict['Recursive'] = recursive
for term in terms:
display.vvv("AWS_ssm path lookup term: %s in region: %s" % (term, region))
paramlist = self.get_path_parameters(client, ssm_dict, term, on_missing.lower(), on_denied.lower())
# Shorten parameter names. Yes, this will return
# duplicate names with different values.
if shortnames:
for x in paramlist:
x['Name'] = x['Name'][x['Name'].rfind('/') + 1:]
display.vvvv("AWS_ssm path lookup returned: %s" % str(paramlist))
ret.append(boto3_tag_list_to_ansible_dict(paramlist,
tag_name_key_name="Name",
tag_value_key_name="Value"))
# Lookup by parameter name - always returns a list with one or
# no entry.
else:
display.vvv("AWS_ssm name lookup term: %s" % terms)
for term in terms:
ret.append(self.get_parameter_value(client, ssm_dict, term, on_missing.lower(), on_denied.lower()))
display.vvvv("AWS_ssm path lookup returning: %s " % str(ret))
return ret
def get_path_parameters(self, client, ssm_dict, term, on_missing, on_denied):
ssm_dict["Path"] = term
paginator = client.get_paginator('get_parameters_by_path')
try:
paramlist = paginator.paginate(**ssm_dict).build_full_result()['Parameters']
except is_boto3_error_code('AccessDeniedException'):
if on_denied == 'error':
raise AnsibleError("Failed to access SSM parameter path %s (AccessDenied)" % term)
elif on_denied == 'warn':
self._display.warning('Skipping, access denied for SSM parameter path %s' % term)
paramlist = [{}]
elif on_denied == 'skip':
paramlist = [{}]
except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except
raise AnsibleError("SSM lookup exception: {0}".format(to_native(e)))
if not len(paramlist):
if on_missing == "error":
raise AnsibleError("Failed to find SSM parameter path %s (ResourceNotFound)" % term)
elif on_missing == "warn":
self._display.warning('Skipping, did not find SSM parameter path %s' % term)
return paramlist
def get_parameter_value(self, client, ssm_dict, term, on_missing, on_denied):
ssm_dict["Name"] = term
try:
response = client.get_parameter(**ssm_dict)
return response['Parameter']['Value']
except is_boto3_error_code('ParameterNotFound'):
if on_missing == 'error':
raise AnsibleError("Failed to find SSM parameter %s (ResourceNotFound)" % term)
elif on_missing == 'warn':
self._display.warning('Skipping, did not find SSM parameter %s' % term)
except is_boto3_error_code('AccessDeniedException'): # pylint: disable=duplicate-except
if on_denied == 'error':
raise AnsibleError("Failed to access SSM parameter %s (AccessDenied)" % term)
elif on_denied == 'warn':
self._display.warning('Skipping, access denied for SSM parameter %s' % term)
except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except
raise AnsibleError("SSM lookup exception: {0}".format(to_native(e)))
return None