Skip to content

Commit

Permalink
aws_secret (lookup) - Add support for handling secrets marked for del…
Browse files Browse the repository at this point in the history
…etion (#455)

aws_secret (lookup) - Add support for handling secrets marked for deletion

SUMMARY
Currently if you try to lookup a secret that's been marked for deletion it throws an uncaught exception.
ISSUE TYPE

Feature Pull Request

COMPONENT NAME
plugins/lookup/aws_secret.py
ADDITIONAL INFORMATION
Lack of support for this triggered a test failure
https://dashboard.zuul.ansible.com/t/ansible/build/69abfb39df9b4f71bc9b32aed8a2529b/log/job-output.txt
2021-08-11 09:04:12.324178 | fedora-34 | TASK [lookup_aws_secret : lookup missing secret] *******************************
2021-08-11 09:04:12.324419 | fedora-34 | task path: /home/zuul/.ansible/collections/ansible_collections/amazon/aws/tests/integration/targets/lookup_aws_secret/tasks/main.yaml:25
2021-08-11 09:04:12.807198 | fedora-34 | exception during Jinja2 execution: Traceback (most recent call last):
2021-08-11 09:04:12.807287 | fedora-34 |   File "/home/zuul/.ansible/collections/ansible_collections/amazon/aws/plugins/lookup/aws_secret.py", line 244, in get_secret_value
2021-08-11 09:04:12.807301 | fedora-34 |     response = client.get_secret_value(**params)
2021-08-11 09:04:12.807311 | fedora-34 |   File "/home/zuul/venv/lib/python3.6/site-packages/botocore/client.py", line 386, in _api_call
2021-08-11 09:04:12.807321 | fedora-34 |     return self._make_api_call(operation_name, kwargs)
2021-08-11 09:04:12.807330 | fedora-34 |   File "/home/zuul/venv/lib/python3.6/site-packages/botocore/client.py", line 705, in _make_api_call
2021-08-11 09:04:12.807339 | fedora-34 |     raise error_class(parsed_response, operation_name)
2021-08-11 09:04:12.807349 | fedora-34 | botocore.errorfactory.InvalidRequestException: An error occurred (InvalidRequestException) when calling the GetSecretValue operation: You can't perform this operation on the secret because it was marked for deletion.
2021-08-11 09:04:12.807359 | fedora-34 |
2021-08-11 09:04:12.807368 | fedora-34 | During handling of the above exception, another exception occurred:
2021-08-11 09:04:12.807377 | fedora-34 |
2021-08-11 09:04:12.807386 | fedora-34 | Traceback (most recent call last):
2021-08-11 09:04:12.807395 | fedora-34 |   File "/tmp/ansible-test-k5oaalo2/ansible/template/__init__.py", line 1014, in _lookup
2021-08-11 09:04:12.807404 | fedora-34 |     ran = instance.run(loop_terms, variables=self._available_variables, **kwargs)
2021-08-11 09:04:12.807413 | fedora-34 |   File "/home/zuul/.ansible/collections/ansible_collections/amazon/aws/plugins/lookup/aws_secret.py", line 220, in run
2021-08-11 09:04:12.807422 | fedora-34 |     on_missing=missing, on_denied=denied, nested=nested)
2021-08-11 09:04:12.807431 | fedora-34 |   File "/home/zuul/.ansible/collections/ansible_collections/amazon/aws/plugins/lookup/aws_secret.py", line 272, in get_secret_value
2021-08-11 09:04:12.807440 | fedora-34 |     raise AnsibleError("Failed to retrieve secret: %s" % to_native(e))
2021-08-11 09:04:12.807449 | fedora-34 | ansible.errors.AnsibleError: Failed to retrieve secret: An error occurred (InvalidRequestException) when calling the GetSecretValue operation: You can't perform this operation on the secret because it was marked for deletion.
2021-08-11 09:04:12.814937 | fedora-34 | fatal: [testhost]: FAILED! => {
2021-08-11 09:04:12.814987 | fedora-34 |     "msg": "An unhandled exception occurred while running the lookup plugin 'amazon.aws.aws_secret'. Error was a <class 'ansible.errors.AnsibleError'>, original message: Failed to retrieve secret: An error occurred (InvalidRequestException) when calling the GetSecretValue operation: You can't perform this operation on the secret because it was marked for deletion.. Failed to retrieve secret: An error occurred (InvalidRequestException) when calling the GetSecretValue operation: You can't perform this operation on the secret because it was marked for deletion."
2021-08-11 09:04:12.815002 | fedora-34 | }
2021-08-11 09:04:12.829452 | fedora-34 |

Depends-On: #460

Reviewed-by: Alina Buzachis <None>
Reviewed-by: Mark Chappell <None>
Reviewed-by: None <None>
  • Loading branch information
tremble committed Aug 12, 2021
1 parent feb9994 commit 54d313b
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 18 deletions.
2 changes: 2 additions & 0 deletions changelogs/fragments/455-lookup_aws_secret-deleted.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
minor_changes:
- aws_secret - added support for gracefully handling deleted secrets (https://github.com/ansible-collections/amazon.aws/pull/455).
30 changes: 26 additions & 4 deletions plugins/lookup/aws_secret.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@
- 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.
Expand Down Expand Up @@ -125,6 +135,7 @@
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


Expand All @@ -148,7 +159,7 @@ 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_denied='error', on_deleted='error'):
'''
:arg terms: a list of lookups to run.
e.g. ['parameter_name', 'parameter_name_too' ]
Expand All @@ -164,12 +175,17 @@ def run(self, terms, variables=None, boto_profile=None, aws_profile=None,
: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('botocore and boto3 are required for aws_ssm lookup.')

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)
Expand Down Expand Up @@ -217,7 +233,8 @@ def run(self, terms, variables=None, boto_profile=None, aws_profile=None,
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, nested=nested)
on_missing=missing, on_denied=denied, on_deleted=deleted,
nested=nested)
if value:
secrets.append(value)
if join:
Expand All @@ -227,7 +244,7 @@ def run(self, terms, variables=None, boto_profile=None, aws_profile=None,

return secrets

def get_secret_value(self, term, client, version_stage=None, version_id=None, on_missing=None, on_denied=None, nested=False):
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:
Expand Down Expand Up @@ -258,7 +275,12 @@ def get_secret_value(self, term, client, version_stage=None, version_id=None, on
return str(ret_val)
else:
return response['SecretString']
except is_boto3_error_code('ResourceNotFoundException'):
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':
Expand Down
69 changes: 55 additions & 14 deletions tests/integration/targets/lookup_aws_secret/tasks/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,41 +18,82 @@
block:
- name: define secret name
set_fact:
secret_name: "ansible-test-{{ resource_prefix | hash('md5') }}-secret"
secret_name: "ansible-test-{{ tiny_prefix }}-secret"
secret_value: "{{ lookup('password', '/dev/null chars=ascii_lowercase,digits,punctuation length=16') }}"
on_missing_secret: "skip"
on_deleted_secret: "skip"

- name: lookup missing secret
- name: lookup missing secret (skip)
set_fact:
missing_secret: "{{ lookup('amazon.aws.aws_secret', secret_name, on_missing=on_missing_secret, **connection_args) }}"
missing_secret: "{{ lookup('amazon.aws.aws_secret', secret_name, on_missing=on_missing_secret, on_deleted=on_deleted_secret, **connection_args) }}"

- name: assert that missing_secret is defined
assert:
that:
- missing_secret is defined
- missing_secret | list | length == 0


- name: lookup missing secret (error)
set_fact:
missing_secret: "{{ lookup('amazon.aws.aws_secret', secret_name, **connection_args) }}"
ignore_errors: True
register: get_missing_secret

- name: assert that setting the missing_secret failed
assert:
that:
- get_missing_secret is failed

- name: create secret "{{ secret_name }}"
aws_secret:
name: "{{ secret_name }}"
secret: "{{ secret_value }}"
tags:
ansible-test: "aws-tests-integration"
state: present

- name: read secret value
set_fact:
look_secret: "{{ lookup('amazon.aws.aws_secret', secret_name, **connection_args) }}"

- name: assert that secret was successfully retrieved
assert:
that:
- look_secret == secret_value


- name: delete secret
aws_secret:
name: "{{ secret_name }}"
state: absent
recovery_window: 7

- name: lookup deleted secret (skip)
set_fact:
deleted_secret: "{{ lookup('amazon.aws.aws_secret', secret_name, on_missing=on_missing_secret, on_deleted=on_deleted_secret, **connection_args) }}"

- name: assert that deleted_secret is defined
assert:
that:
- deleted_secret is defined
- deleted_secret | list | length == 0

- name: lookup deleted secret (error)
set_fact:
missing_secret: "{{ lookup('amazon.aws.aws_secret', secret_name, **connection_args) }}"
ignore_errors: True
register: get_deleted_secret

- name: assert that setting the deleted_secret failed
assert:
that:
- get_deleted_secret is failed

always:
# delete secret created
- name: delete secret
aws_secret:
name: "{{ secret_name }}"
state: absent
ignore_errors: yes

# delete secret created
- name: delete secret
aws_secret:
name: "{{ secret_name }}"
state: absent
recovery_window: 0
ignore_errors: yes

0 comments on commit 54d313b

Please sign in to comment.