From 968f7f9e69bbad40c17ae922192836655684dbe1 Mon Sep 17 00:00:00 2001 From: Andrew Roth Date: Fri, 10 Nov 2023 09:41:06 -0500 Subject: [PATCH 1/7] added lookup to generate random password --- .../lookup/secretsmanager_random_password.py | 191 ++++++++++++++++++ .../aliases | 1 + .../meta/main.yml | 1 + .../tasks/main.yaml | 41 ++++ 4 files changed, 234 insertions(+) create mode 100644 plugins/lookup/secretsmanager_random_password.py create mode 100644 tests/integration/targets/lookup_secretsmanager_random_password/aliases create mode 100644 tests/integration/targets/lookup_secretsmanager_random_password/meta/main.yml create mode 100644 tests/integration/targets/lookup_secretsmanager_random_password/tasks/main.yaml diff --git a/plugins/lookup/secretsmanager_random_password.py b/plugins/lookup/secretsmanager_random_password.py new file mode 100644 index 00000000000..11a31feaf30 --- /dev/null +++ b/plugins/lookup/secretsmanager_random_password.py @@ -0,0 +1,191 @@ +# -*- coding: utf-8 -*- + +# Copyright: (c) 2023, Andrew Roth +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r""" +name: secretsmanager_random_password +author: + - Andrew Roth + +short_description: Generate a random password using AWS Secrets Manager +description: + - Look up (really generate) a random password using AWS Secrets Manager's + `secretsmanager:GetRandomPassword` API. + - Optional parameters can be passed into this lookup; I(password_length) and I(exclude_characters) + +options: + _terms: + description: As a shortcut, the password_length parameter can be specified as a term instead of using the keyword. + required: False + type: integer + password_length: + description: The length of the password. If you don’t include this parameter, the default length is 32 characters. + required: False + type: integer + exclude_characters: + description: A string of the characters that you don’t want in the password. + required: False + type: string + exclude_numbers: + description: Specifies whether to exclude numbers from the password (included by default). + required: False + type: boolean + exclude_punctuation: + description: |- + Specifies whether to exclude punctuation characters from the password: + `! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~` (included by default). + required: False + type: boolean + exclude_uppercase: + description: Specifies whether to exclude uppercase letters from the password (included by default). + required: False + type: boolean + exclude_lowercase: + description: Specifies whether to exclude lowercase letters from the password (included by default). + required: False + type: boolean + include_space: + description: Specifies whether to include the space character (excluded by default). + required: False + type: boolean + require_each_included_type: + description: Specifies whether to include at least one upper and lowercase letter, one number, and one punctuation. + required: False + type: boolean + 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'] +extends_documentation_fragment: + - amazon.aws.boto3 + - amazon.aws.common.plugins + - amazon.aws.region.plugins +""" + +EXAMPLES = r""" + - name: generate random password + debug: msg="{{ lookup('secretsmanager_random_password') }}" + + - name: generate random 12-character password without punctuation + debug: msg="{{ lookup('secretsmanager_random_password', 12, exclude_punctuation=True) }}" + + - name: create a secret using a random password + community.aws.secretsmanager_secret: + name: 'test_secret_string' + state: present + secret_type: 'string' + secret: "{{ lookup('secretsmanager_random_password') }}" +""" + +RETURN = r""" +_raw: + description: + Returns the random password. This password is not saved and will always be new. +""" + +try: + import botocore +except ImportError: + pass # Handled by AWSLookupBase + +from ansible.errors import AnsibleLookupError +from ansible.module_utils._text import to_native +from ansible.module_utils.six import string_types + +from ansible_collections.amazon.aws.plugins.module_utils.botocore import is_boto3_error_code +from ansible_collections.amazon.aws.plugins.module_utils.retries import AWSRetry +from ansible_collections.amazon.aws.plugins.plugin_utils.lookup import AWSLookupBase + + +class LookupModule(AWSLookupBase): + def run(self, terms, variables=None, **kwargs): + """ + :param terms: a list containing the password length + e.g. ['example_secret_name', 'example_secret_too' ] + :param variables: ansible variables active at the time of the lookup + :returns: A list of parameter values or a list of dictionaries if bypath=True. + """ + + super().run(terms, variables, **kwargs) + + # validate argument terms + if len(terms) > 1: + raise AnsibleLookupError( + f'secretsmanager_random_password must have zero or one argument' + ) + + on_denied = self.get_option("on_denied") + + # validate arguments 'on_denied' + if on_denied is not None and ( + not isinstance(on_denied, string_types) or on_denied.lower() not in ["error", "warn", "skip"] + ): + raise AnsibleLookupError( + f'"on_denied" must be a string and one of "error", "warn" or "skip", not {on_denied}' + ) + + params = {} + # validate password length argument or option + self.debug(f"Options: {self.get_options()}") + password_length = self.get_option("password_length") + if len(terms) == 1: + if password_length is not None: + raise AnsibleLookupError( + f'"password_length" should be provided as argument or keyword, not both' + ) + password_length = terms[0] + if password_length is not None: + if not isinstance(password_length, string_types): + raise AnsibleLookupError( + f'"password_length" must be a string, if provided' + ) + params["PasswordLength"] = password_length + + # validate exclude characters + exclude_characters = self.get_option("exclude_characters") + if exclude_characters is not None: + if not isinstance(exclude_characters, string_types): + raise AnsibleLookupError( + f'"exclude_characters" must be a string, if provided' + ) + params["ExcludeCharacters"] = exclude_characters + + # validate options for parameters + bool_options_to_params = { + "exclude_numbers": "ExcludeNumbers", + "exclude_punctuation": "ExcludePunctuation", + "exclude_uppercase": "ExcludeUppercase", + "exclude_lowercase": "ExcludeLowercase", + "include_space": "IncludeSpace", + "require_each_included_type": "RequireEachIncludedType", + } + for opt_name in bool_options_to_params.keys(): + opt_value = self.get_option(opt_name) + if opt_value is not None: + if not isinstance(opt_value, bool): + raise AnsibleLookupError( + f'"{opt_name}" must be a boolean value, if provided' + ) + params[bool_options_to_params[opt_name]] = opt_value + + client = self.client("secretsmanager", AWSRetry.jittered_backoff()) + + try: + response = client.get_random_password(**params) + return [response['RandomPassword']] + except is_boto3_error_code("AccessDeniedException"): # pylint: disable=duplicate-except + if on_denied == "error": + raise AnsibleLookupError(f"Failed to generate random password (AccessDenied)") + elif on_denied == "warn": + self._display.warning(f"Skipping, access denied to generate random password") + except ( + botocore.exceptions.ClientError, + botocore.exceptions.BotoCoreError, + ) as e: # pylint: disable=duplicate-except + raise AnsibleLookupError(f"Failed to retrieve secret: {to_native(e)}") diff --git a/tests/integration/targets/lookup_secretsmanager_random_password/aliases b/tests/integration/targets/lookup_secretsmanager_random_password/aliases new file mode 100644 index 00000000000..4ef4b2067d0 --- /dev/null +++ b/tests/integration/targets/lookup_secretsmanager_random_password/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/tests/integration/targets/lookup_secretsmanager_random_password/meta/main.yml b/tests/integration/targets/lookup_secretsmanager_random_password/meta/main.yml new file mode 100644 index 00000000000..32cf5dda7ed --- /dev/null +++ b/tests/integration/targets/lookup_secretsmanager_random_password/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/tests/integration/targets/lookup_secretsmanager_random_password/tasks/main.yaml b/tests/integration/targets/lookup_secretsmanager_random_password/tasks/main.yaml new file mode 100644 index 00000000000..6d0de9232e2 --- /dev/null +++ b/tests/integration/targets/lookup_secretsmanager_random_password/tasks/main.yaml @@ -0,0 +1,41 @@ +- set_fact: + # As a lookup plugin we don't have access to module_defaults + connection_args: + region: "{{ aws_region }}" + access_key: "{{ aws_access_key }}" + secret_key: "{{ aws_secret_key }}" + session_token: "{{ security_token | default(omit) }}" + no_log: True + +- module_defaults: + group/aws: + region: "{{ aws_region }}" + access_key: "{{ aws_access_key }}" + secret_key: "{{ aws_secret_key }}" + session_token: "{{ security_token | default(omit) }}" + collections: + - amazon.aws + - community.aws + block: + + - name: generate random password + set_fact: + gen_pass: "{{ lookup('amazon.aws.secretsmanager_random_password', **connection_args) }}" + + - name: assert that random password was successfully retrieved + assert: + that: + - gen_pass is defined + - gen_pass is string + - "{{ gen_pass | length }}" is 32 + + - name: generate random password length 12 + set_fact: + gen_pass: "{{ lookup('amazon.aws.secretsmanager_random_password', 12, **connection_args) }}" + + - name: assert that random password length 12 was successfully retrieved + assert: + that: + - gen_pass is defined + - gen_pass is string + - "{{ gen_pass | length }}" is 12 From 30de5c4e9dc6c913366db40d0bccf0dfbb8b7aed Mon Sep 17 00:00:00 2001 From: Andrew Roth Date: Fri, 10 Nov 2023 13:58:42 -0500 Subject: [PATCH 2/7] fixed error in code and integration tests for lookup secretsmanager_random_password --- .../lookup/secretsmanager_random_password.py | 6 +- .../tasks/main.yaml | 67 +++++++++++++++++-- 2 files changed, 64 insertions(+), 9 deletions(-) diff --git a/plugins/lookup/secretsmanager_random_password.py b/plugins/lookup/secretsmanager_random_password.py index 11a31feaf30..08dc22f0f8e 100644 --- a/plugins/lookup/secretsmanager_random_password.py +++ b/plugins/lookup/secretsmanager_random_password.py @@ -96,7 +96,7 @@ from ansible.errors import AnsibleLookupError from ansible.module_utils._text import to_native -from ansible.module_utils.six import string_types +from ansible.module_utils.six import string_types, integer_types from ansible_collections.amazon.aws.plugins.module_utils.botocore import is_boto3_error_code from ansible_collections.amazon.aws.plugins.module_utils.retries import AWSRetry @@ -141,9 +141,9 @@ def run(self, terms, variables=None, **kwargs): ) password_length = terms[0] if password_length is not None: - if not isinstance(password_length, string_types): + if not isinstance(password_length, integer_types) or password_length < 1: raise AnsibleLookupError( - f'"password_length" must be a string, if provided' + f'"password_length" must be an integer greater than zero, if provided' ) params["PasswordLength"] = password_length diff --git a/tests/integration/targets/lookup_secretsmanager_random_password/tasks/main.yaml b/tests/integration/targets/lookup_secretsmanager_random_password/tasks/main.yaml index 6d0de9232e2..6307c7fce55 100644 --- a/tests/integration/targets/lookup_secretsmanager_random_password/tasks/main.yaml +++ b/tests/integration/targets/lookup_secretsmanager_random_password/tasks/main.yaml @@ -5,7 +5,10 @@ access_key: "{{ aws_access_key }}" secret_key: "{{ aws_secret_key }}" session_token: "{{ security_token | default(omit) }}" - no_log: True + chars_punct: "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~" + chars_upper: "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + chars_lower: "abcdefghijklmnopqrstuvwxyz" + chars_numbs: "0123456789" - module_defaults: group/aws: @@ -20,22 +23,74 @@ - name: generate random password set_fact: - gen_pass: "{{ lookup('amazon.aws.secretsmanager_random_password', **connection_args) }}" + gen_pass: "{{ lookup('community.aws.secretsmanager_random_password', **connection_args) }}" - name: assert that random password was successfully retrieved assert: that: - gen_pass is defined - gen_pass is string - - "{{ gen_pass | length }}" is 32 + - gen_pass|length == 32 - name: generate random password length 12 set_fact: - gen_pass: "{{ lookup('amazon.aws.secretsmanager_random_password', 12, **connection_args) }}" + gen_pass: "{{ lookup('community.aws.secretsmanager_random_password', 12, **connection_args) }}" - name: assert that random password length 12 was successfully retrieved assert: that: - - gen_pass is defined - gen_pass is string - - "{{ gen_pass | length }}" is 12 + - gen_pass|length == 12 + + - name: generate random password without punctuation + set_fact: + gen_pass: "{{ lookup('community.aws.secretsmanager_random_password', exclude_punctuation=True, **connection_args) }}" + + - name: assert that random password is without punctuation + assert: + that: + - gen_pass is string + - gen_pass|intersect(chars_punct)|length == 0 + + - name: generate random password without uppercase letters + set_fact: + gen_pass: "{{ lookup('community.aws.secretsmanager_random_password', exclude_uppercase=True, **connection_args) }}" + + - name: assert that random password is without uppercase + assert: + that: + - gen_pass is string + - gen_pass|intersect(chars_upper)|length == 0 + + - name: generate random password without lowercase + set_fact: + gen_pass: "{{ lookup('community.aws.secretsmanager_random_password', exclude_lowercase=True, **connection_args) }}" + + - name: assert that random password is without lowercase + assert: + that: + - gen_pass is string + - gen_pass|intersect(chars_lower)|length == 0 + + - name: generate random password without numbers + set_fact: + gen_pass: "{{ lookup('community.aws.secretsmanager_random_password', exclude_numbers=True, **connection_args) }}" + + - name: assert that random password is without numbers + assert: + that: + - gen_pass is string + - gen_pass|intersect(chars_numbs)|length == 0 + + - name: generate random password with a space + # all but numbers are excluded to increase the probability of a space being randomly included. + set_fact: + gen_pass: "{{ lookup('community.aws.secretsmanager_random_password', exclude_punctuation=True, exclude_uppercase=True, exclude_lowercase=True, include_space=True, **connection_args) }}" + + - name: assert that random password has a space + # while this has a high probability of passing, it is possible that a space might not be included in the random password + # please re-run the test to confirm if the failure is random or real. + assert: + that: + - gen_pass is string + - gen_pass|intersect(" ")|length > 0 From 927b1c9c74778441dd2a341a4531501264d2666d Mon Sep 17 00:00:00 2001 From: Andrew Roth Date: Fri, 10 Nov 2023 17:15:00 -0500 Subject: [PATCH 3/7] added no_log to integration test to mask aws keys --- .../lookup_secretsmanager_random_password/tasks/main.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integration/targets/lookup_secretsmanager_random_password/tasks/main.yaml b/tests/integration/targets/lookup_secretsmanager_random_password/tasks/main.yaml index 6307c7fce55..79c5f1fa8e8 100644 --- a/tests/integration/targets/lookup_secretsmanager_random_password/tasks/main.yaml +++ b/tests/integration/targets/lookup_secretsmanager_random_password/tasks/main.yaml @@ -9,6 +9,7 @@ chars_upper: "ABCDEFGHIJKLMNOPQRSTUVWXYZ" chars_lower: "abcdefghijklmnopqrstuvwxyz" chars_numbs: "0123456789" + no_log: True - module_defaults: group/aws: From 2dda8011e218d55e2d9163b0a1a66288aeb42d8a Mon Sep 17 00:00:00 2001 From: Andrew Roth Date: Fri, 14 Jun 2024 14:38:23 -0400 Subject: [PATCH 4/7] fix pylint formatting errors --- .../lookup/secretsmanager_random_password.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/plugins/lookup/secretsmanager_random_password.py b/plugins/lookup/secretsmanager_random_password.py index 08dc22f0f8e..3d48e77b80a 100644 --- a/plugins/lookup/secretsmanager_random_password.py +++ b/plugins/lookup/secretsmanager_random_password.py @@ -6,11 +6,11 @@ DOCUMENTATION = r""" name: secretsmanager_random_password author: - - Andrew Roth + - Andrew Roth (@andrewjroth) short_description: Generate a random password using AWS Secrets Manager description: - - Look up (really generate) a random password using AWS Secrets Manager's + - Look up (really generate) a random password using AWS Secrets Manager's `secretsmanager:GetRandomPassword` API. - Optional parameters can be passed into this lookup; I(password_length) and I(exclude_characters) @@ -20,11 +20,11 @@ required: False type: integer password_length: - description: The length of the password. If you don’t include this parameter, the default length is 32 characters. + description: The length of the password. If you do not include this parameter, the default length is 32 characters. required: False type: integer exclude_characters: - description: A string of the characters that you don’t want in the password. + description: A string of the characters that you do not want in the password. required: False type: string exclude_numbers: @@ -33,7 +33,7 @@ type: boolean exclude_punctuation: description: |- - Specifies whether to exclude punctuation characters from the password: + Specifies whether to exclude punctuation characters from the password: `! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~` (included by default). required: False type: boolean @@ -74,7 +74,7 @@ - name: generate random 12-character password without punctuation debug: msg="{{ lookup('secretsmanager_random_password', 12, exclude_punctuation=True) }}" - + - name: create a secret using a random password community.aws.secretsmanager_secret: name: 'test_secret_string' @@ -117,7 +117,7 @@ def run(self, terms, variables=None, **kwargs): # validate argument terms if len(terms) > 1: raise AnsibleLookupError( - f'secretsmanager_random_password must have zero or one argument' + "secretsmanager_random_password must have zero or one argument" ) on_denied = self.get_option("on_denied") @@ -137,13 +137,13 @@ def run(self, terms, variables=None, **kwargs): if len(terms) == 1: if password_length is not None: raise AnsibleLookupError( - f'"password_length" should be provided as argument or keyword, not both' + '"password_length" should be provided as argument or keyword, not both' ) password_length = terms[0] if password_length is not None: if not isinstance(password_length, integer_types) or password_length < 1: raise AnsibleLookupError( - f'"password_length" must be an integer greater than zero, if provided' + '"password_length" must be an integer greater than zero, if provided' ) params["PasswordLength"] = password_length @@ -152,7 +152,7 @@ def run(self, terms, variables=None, **kwargs): if exclude_characters is not None: if not isinstance(exclude_characters, string_types): raise AnsibleLookupError( - f'"exclude_characters" must be a string, if provided' + '"exclude_characters" must be a string, if provided' ) params["ExcludeCharacters"] = exclude_characters @@ -181,9 +181,9 @@ def run(self, terms, variables=None, **kwargs): return [response['RandomPassword']] except is_boto3_error_code("AccessDeniedException"): # pylint: disable=duplicate-except if on_denied == "error": - raise AnsibleLookupError(f"Failed to generate random password (AccessDenied)") + raise AnsibleLookupError("Failed to generate random password (AccessDenied)") elif on_denied == "warn": - self._display.warning(f"Skipping, access denied to generate random password") + self._display.warning("Skipping, access denied to generate random password") except ( botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError, From db8a8900318d732d13e09bdd16ea042f055a4afd Mon Sep 17 00:00:00 2001 From: Andrew Roth Date: Mon, 17 Jun 2024 10:44:10 -0400 Subject: [PATCH 5/7] fix lint errors, I think --- .../lookup/secretsmanager_random_password.py | 25 ++++++------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/plugins/lookup/secretsmanager_random_password.py b/plugins/lookup/secretsmanager_random_password.py index 3d48e77b80a..6ab3e77cfb0 100644 --- a/plugins/lookup/secretsmanager_random_password.py +++ b/plugins/lookup/secretsmanager_random_password.py @@ -96,7 +96,8 @@ from ansible.errors import AnsibleLookupError from ansible.module_utils._text import to_native -from ansible.module_utils.six import string_types, integer_types +from ansible.module_utils.six import string_types +from ansible.module_utils.six import integer_types from ansible_collections.amazon.aws.plugins.module_utils.botocore import is_boto3_error_code from ansible_collections.amazon.aws.plugins.module_utils.retries import AWSRetry @@ -116,9 +117,7 @@ def run(self, terms, variables=None, **kwargs): # validate argument terms if len(terms) > 1: - raise AnsibleLookupError( - "secretsmanager_random_password must have zero or one argument" - ) + raise AnsibleLookupError("secretsmanager_random_password must have zero or one argument") on_denied = self.get_option("on_denied") @@ -136,24 +135,18 @@ def run(self, terms, variables=None, **kwargs): password_length = self.get_option("password_length") if len(terms) == 1: if password_length is not None: - raise AnsibleLookupError( - '"password_length" should be provided as argument or keyword, not both' - ) + raise AnsibleLookupError('"password_length" should be provided as argument or keyword, not both') password_length = terms[0] if password_length is not None: if not isinstance(password_length, integer_types) or password_length < 1: - raise AnsibleLookupError( - '"password_length" must be an integer greater than zero, if provided' - ) + raise AnsibleLookupError('"password_length" must be an integer greater than zero, if provided') params["PasswordLength"] = password_length # validate exclude characters exclude_characters = self.get_option("exclude_characters") if exclude_characters is not None: if not isinstance(exclude_characters, string_types): - raise AnsibleLookupError( - '"exclude_characters" must be a string, if provided' - ) + raise AnsibleLookupError('"exclude_characters" must be a string, if provided') params["ExcludeCharacters"] = exclude_characters # validate options for parameters @@ -169,16 +162,14 @@ def run(self, terms, variables=None, **kwargs): opt_value = self.get_option(opt_name) if opt_value is not None: if not isinstance(opt_value, bool): - raise AnsibleLookupError( - f'"{opt_name}" must be a boolean value, if provided' - ) + raise AnsibleLookupError(f'"{opt_name}" must be a boolean value, if provided') params[bool_options_to_params[opt_name]] = opt_value client = self.client("secretsmanager", AWSRetry.jittered_backoff()) try: response = client.get_random_password(**params) - return [response['RandomPassword']] + return [response["RandomPassword"]] except is_boto3_error_code("AccessDeniedException"): # pylint: disable=duplicate-except if on_denied == "error": raise AnsibleLookupError("Failed to generate random password (AccessDenied)") From da0d2db3a687e10b29f88886db183840494180ad Mon Sep 17 00:00:00 2001 From: Andrew Roth Date: Mon, 12 Aug 2024 17:20:27 -0400 Subject: [PATCH 6/7] fix whitespace in examples --- .../lookup/secretsmanager_random_password.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/plugins/lookup/secretsmanager_random_password.py b/plugins/lookup/secretsmanager_random_password.py index 6ab3e77cfb0..0953b2c633f 100644 --- a/plugins/lookup/secretsmanager_random_password.py +++ b/plugins/lookup/secretsmanager_random_password.py @@ -69,18 +69,18 @@ """ EXAMPLES = r""" - - name: generate random password - debug: msg="{{ lookup('secretsmanager_random_password') }}" - - - name: generate random 12-character password without punctuation - debug: msg="{{ lookup('secretsmanager_random_password', 12, exclude_punctuation=True) }}" - - - name: create a secret using a random password - community.aws.secretsmanager_secret: - name: 'test_secret_string' - state: present - secret_type: 'string' - secret: "{{ lookup('secretsmanager_random_password') }}" + - name: generate random password + debug: msg="{{ lookup('secretsmanager_random_password') }}" + + - name: generate random 12-character password without punctuation + debug: msg="{{ lookup('secretsmanager_random_password', 12, exclude_punctuation=True) }}" + + - name: create a secret using a random password + community.aws.secretsmanager_secret: + name: 'test_secret_string' + state: present + secret_type: 'string' + secret: "{{ lookup('secretsmanager_random_password') }}" """ RETURN = r""" From 8122c1e1dea5910961864ad6bf35f14d1b2cff76 Mon Sep 17 00:00:00 2001 From: Andrew Roth Date: Tue, 13 Aug 2024 10:54:15 -0400 Subject: [PATCH 7/7] removed password length from terms; updated tests to not check default length; updated documentation --- .../lookup/secretsmanager_random_password.py | 30 ++++++++----------- .../tasks/main.yaml | 3 +- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/plugins/lookup/secretsmanager_random_password.py b/plugins/lookup/secretsmanager_random_password.py index 0953b2c633f..664d3eaaac0 100644 --- a/plugins/lookup/secretsmanager_random_password.py +++ b/plugins/lookup/secretsmanager_random_password.py @@ -11,16 +11,15 @@ short_description: Generate a random password using AWS Secrets Manager description: - Look up (really generate) a random password using AWS Secrets Manager's - `secretsmanager:GetRandomPassword` API. - - Optional parameters can be passed into this lookup; I(password_length) and I(exclude_characters) + C(secretsmanager:GetRandomPassword) API. + - See U(https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetRandomPassword.html#API_GetRandomPassword_RequestParameters) + for information about the API for GetRandomPassword and how it can be used. options: - _terms: - description: As a shortcut, the password_length parameter can be specified as a term instead of using the keyword. - required: False - type: integer password_length: - description: The length of the password. If you do not include this parameter, the default length is 32 characters. + description: |- + The length of the password. If you do not include this parameter, + AWS will use a default value according to the API documentation (see link in description above). required: False type: integer exclude_characters: @@ -34,7 +33,7 @@ exclude_punctuation: description: |- Specifies whether to exclude punctuation characters from the password: - `! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~` (included by default). + C(! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~) (included by default). required: False type: boolean exclude_uppercase: @@ -73,7 +72,7 @@ debug: msg="{{ lookup('secretsmanager_random_password') }}" - name: generate random 12-character password without punctuation - debug: msg="{{ lookup('secretsmanager_random_password', 12, exclude_punctuation=True) }}" + debug: msg="{{ lookup('secretsmanager_random_password', password_length=12, exclude_punctuation=True) }}" - name: create a secret using a random password community.aws.secretsmanager_secret: @@ -107,8 +106,7 @@ class LookupModule(AWSLookupBase): def run(self, terms, variables=None, **kwargs): """ - :param terms: a list containing the password length - e.g. ['example_secret_name', 'example_secret_too' ] + :param terms: an empty list (does not use) :param variables: ansible variables active at the time of the lookup :returns: A list of parameter values or a list of dictionaries if bypath=True. """ @@ -116,8 +114,8 @@ def run(self, terms, variables=None, **kwargs): super().run(terms, variables, **kwargs) # validate argument terms - if len(terms) > 1: - raise AnsibleLookupError("secretsmanager_random_password must have zero or one argument") + if len(terms) > 0: + raise AnsibleLookupError("secretsmanager_random_password does not accept positional arguments") on_denied = self.get_option("on_denied") @@ -130,13 +128,9 @@ def run(self, terms, variables=None, **kwargs): ) params = {} - # validate password length argument or option + # validate password length option self.debug(f"Options: {self.get_options()}") password_length = self.get_option("password_length") - if len(terms) == 1: - if password_length is not None: - raise AnsibleLookupError('"password_length" should be provided as argument or keyword, not both') - password_length = terms[0] if password_length is not None: if not isinstance(password_length, integer_types) or password_length < 1: raise AnsibleLookupError('"password_length" must be an integer greater than zero, if provided') diff --git a/tests/integration/targets/lookup_secretsmanager_random_password/tasks/main.yaml b/tests/integration/targets/lookup_secretsmanager_random_password/tasks/main.yaml index 79c5f1fa8e8..e40f1f1eced 100644 --- a/tests/integration/targets/lookup_secretsmanager_random_password/tasks/main.yaml +++ b/tests/integration/targets/lookup_secretsmanager_random_password/tasks/main.yaml @@ -31,11 +31,10 @@ that: - gen_pass is defined - gen_pass is string - - gen_pass|length == 32 - name: generate random password length 12 set_fact: - gen_pass: "{{ lookup('community.aws.secretsmanager_random_password', 12, **connection_args) }}" + gen_pass: "{{ lookup('community.aws.secretsmanager_random_password', password_length=12, **connection_args) }}" - name: assert that random password length 12 was successfully retrieved assert: