Add AWS Inspector Target Module (#37464)
* Add AWS Inspector Target Module * "ansible-test sanity" Fixes * * Rename module * Add integration test * Incorporate feedback from s-hertel
This commit is contained in:
parent
4134f1204b
commit
77f5a8f422
4 changed files with 347 additions and 0 deletions
246
lib/ansible/modules/cloud/amazon/aws_inspector_target.py
Normal file
246
lib/ansible/modules/cloud/amazon/aws_inspector_target.py
Normal file
|
@ -0,0 +1,246 @@
|
|||
#!/usr/bin/python
|
||||
# Copyright (c) 2018 Dennis Conrad for Sainsbury's
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: aws_inspector_target
|
||||
short_description: Create, Update and Delete Amazon Inspector Assessment
|
||||
Targets
|
||||
description: Creates, updates, or deletes Amazon Inspector Assessment Targets
|
||||
and manages the required Resource Groups.
|
||||
version_added: "2.6"
|
||||
author: "Dennis Conrad (@dennisconrad)"
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- The user-defined name that identifies the assessment target. The name
|
||||
must be unique within the AWS account.
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- The state of the assessment target.
|
||||
choices:
|
||||
- absent
|
||||
- present
|
||||
default: present
|
||||
tags:
|
||||
description:
|
||||
- Tags of the EC2 instances to be added to the assessment target.
|
||||
- Required if C(state=present).
|
||||
extends_documentation_fragment:
|
||||
- aws
|
||||
- ec2
|
||||
requirements:
|
||||
- boto3
|
||||
- botocore
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Create my_target Assessment Target
|
||||
aws_inspector_target:
|
||||
name: my_target
|
||||
tags:
|
||||
role: scan_target
|
||||
|
||||
- name: Update Existing my_target Assessment Target with Additional Tags
|
||||
aws_inspector_target:
|
||||
name: my_target
|
||||
tags:
|
||||
env: dev
|
||||
role: scan_target
|
||||
|
||||
- name: Delete my_target Assessment Target
|
||||
aws_inspector_target:
|
||||
name: my_target
|
||||
state: absent
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
arn:
|
||||
description: The ARN that specifies the Amazon Inspector assessment target.
|
||||
returned: success
|
||||
type: string
|
||||
sample: "arn:aws:inspector:eu-west-1:123456789012:target/0-O4LnL7n1"
|
||||
created_at:
|
||||
description: The time at which the assessment target was created.
|
||||
returned: success
|
||||
type: string
|
||||
sample: "2018-01-29T13:48:51.958000+00:00"
|
||||
name:
|
||||
description: The name of the Amazon Inspector assessment target.
|
||||
returned: success
|
||||
type: string
|
||||
sample: "my_target"
|
||||
resource_group_arn:
|
||||
description: The ARN that specifies the resource group that is associated
|
||||
with the assessment target.
|
||||
returned: success
|
||||
type: string
|
||||
sample: "arn:aws:inspector:eu-west-1:123456789012:resourcegroup/0-qY4gDel8"
|
||||
tags:
|
||||
description: The tags of the resource group that is associated with the
|
||||
assessment target.
|
||||
returned: success
|
||||
type: list
|
||||
sample: {"role": "scan_target", "env": "dev"}
|
||||
updated_at:
|
||||
description: The time at which the assessment target was last updated.
|
||||
returned: success
|
||||
type: string
|
||||
sample: "2018-01-29T13:48:51.958000+00:00"
|
||||
'''
|
||||
|
||||
from ansible.module_utils.aws.core import AnsibleAWSModule
|
||||
from ansible.module_utils.ec2 import AWSRetry
|
||||
from ansible.module_utils.ec2 import (
|
||||
HAS_BOTO3,
|
||||
ansible_dict_to_boto3_tag_list,
|
||||
boto3_tag_list_to_ansible_dict,
|
||||
camel_dict_to_snake_dict,
|
||||
compare_aws_tags,
|
||||
)
|
||||
|
||||
try:
|
||||
import botocore
|
||||
except ImportError:
|
||||
pass # caught by imported HAS_BOTO3
|
||||
|
||||
|
||||
@AWSRetry.backoff(tries=5, delay=5, backoff=2.0)
|
||||
def main():
|
||||
argument_spec = dict(
|
||||
name=dict(required=True),
|
||||
state=dict(choices=['absent', 'present'], default='present'),
|
||||
tags=dict(type='dict'),
|
||||
)
|
||||
|
||||
required_if = [['state', 'present', ['tags']]]
|
||||
|
||||
module = AnsibleAWSModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=False,
|
||||
required_if=required_if,
|
||||
)
|
||||
|
||||
if not HAS_BOTO3:
|
||||
module.fail_json(msg='boto3 and botocore are required for this module')
|
||||
|
||||
name = module.params.get('name')
|
||||
state = module.params.get('state').lower()
|
||||
tags = module.params.get('tags')
|
||||
if tags:
|
||||
tags = ansible_dict_to_boto3_tag_list(tags, 'key', 'value')
|
||||
|
||||
client = module.client('inspector')
|
||||
|
||||
try:
|
||||
existing_target_arn = client.list_assessment_targets(
|
||||
filter={'assessmentTargetNamePattern': name},
|
||||
).get('assessmentTargetArns')[0]
|
||||
|
||||
existing_target = camel_dict_to_snake_dict(
|
||||
client.describe_assessment_targets(
|
||||
assessmentTargetArns=[existing_target_arn],
|
||||
).get('assessmentTargets')[0]
|
||||
)
|
||||
|
||||
existing_resource_group_arn = existing_target.get('resource_group_arn')
|
||||
existing_resource_group_tags = client.describe_resource_groups(
|
||||
resourceGroupArns=[existing_resource_group_arn],
|
||||
).get('resourceGroups')[0].get('tags')
|
||||
|
||||
target_exists = True
|
||||
except (
|
||||
botocore.exceptions.BotoCoreError,
|
||||
botocore.exceptions.ClientError,
|
||||
) as e:
|
||||
module.fail_json_aws(e, msg="trying to retrieve targets")
|
||||
except IndexError:
|
||||
target_exists = False
|
||||
|
||||
if state == 'present' and target_exists:
|
||||
ansible_dict_tags = boto3_tag_list_to_ansible_dict(tags)
|
||||
ansible_dict_existing_tags = boto3_tag_list_to_ansible_dict(
|
||||
existing_resource_group_tags
|
||||
)
|
||||
tags_to_add, tags_to_remove = compare_aws_tags(
|
||||
ansible_dict_tags,
|
||||
ansible_dict_existing_tags
|
||||
)
|
||||
if not (tags_to_add or tags_to_remove):
|
||||
existing_target.update({'tags': ansible_dict_existing_tags})
|
||||
module.exit_json(changed=False, **existing_target)
|
||||
else:
|
||||
try:
|
||||
updated_resource_group_arn = client.create_resource_group(
|
||||
resourceGroupTags=tags,
|
||||
).get('resourceGroupArn')
|
||||
|
||||
client.update_assessment_target(
|
||||
assessmentTargetArn=existing_target_arn,
|
||||
assessmentTargetName=name,
|
||||
resourceGroupArn=updated_resource_group_arn,
|
||||
)
|
||||
|
||||
updated_target = camel_dict_to_snake_dict(
|
||||
client.describe_assessment_targets(
|
||||
assessmentTargetArns=[existing_target_arn],
|
||||
).get('assessmentTargets')[0]
|
||||
)
|
||||
|
||||
updated_target.update({'tags': ansible_dict_tags})
|
||||
module.exit_json(changed=True, **updated_target),
|
||||
except (
|
||||
botocore.exceptions.BotoCoreError,
|
||||
botocore.exceptions.ClientError,
|
||||
) as e:
|
||||
module.fail_json_aws(e, msg="trying to update target")
|
||||
|
||||
elif state == 'present' and not target_exists:
|
||||
try:
|
||||
new_resource_group_arn = client.create_resource_group(
|
||||
resourceGroupTags=tags,
|
||||
).get('resourceGroupArn')
|
||||
|
||||
new_target_arn = client.create_assessment_target(
|
||||
assessmentTargetName=name,
|
||||
resourceGroupArn=new_resource_group_arn,
|
||||
).get('assessmentTargetArn')
|
||||
|
||||
new_target = camel_dict_to_snake_dict(
|
||||
client.describe_assessment_targets(
|
||||
assessmentTargetArns=[new_target_arn],
|
||||
).get('assessmentTargets')[0]
|
||||
)
|
||||
|
||||
new_target.update({'tags': boto3_tag_list_to_ansible_dict(tags)})
|
||||
module.exit_json(changed=True, **new_target)
|
||||
except (
|
||||
botocore.exceptions.BotoCoreError,
|
||||
botocore.exceptions.ClientError,
|
||||
) as e:
|
||||
module.fail_json_aws(e, msg="trying to create target")
|
||||
|
||||
elif state == 'absent' and target_exists:
|
||||
try:
|
||||
client.delete_assessment_target(
|
||||
assessmentTargetArn=existing_target_arn,
|
||||
)
|
||||
module.exit_json(changed=True)
|
||||
except (
|
||||
botocore.exceptions.BotoCoreError,
|
||||
botocore.exceptions.ClientError,
|
||||
) as e:
|
||||
module.fail_json_aws(e, msg="trying to delete target")
|
||||
|
||||
elif state == 'absent' and not target_exists:
|
||||
module.exit_json(changed=False)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
2
test/integration/targets/aws_inspector/aliases
Normal file
2
test/integration/targets/aws_inspector/aliases
Normal file
|
@ -0,0 +1,2 @@
|
|||
cloud/aws
|
||||
posix/ci/cloud/group3/aws
|
3
test/integration/targets/aws_inspector/defaults/main.yml
Normal file
3
test/integration/targets/aws_inspector/defaults/main.yml
Normal file
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
|
||||
aws_inspector_scan_name: "aws_inspector_scan-{{ ansible_date_time.epoch }}"
|
96
test/integration/targets/aws_inspector/tasks/main.yml
Normal file
96
test/integration/targets/aws_inspector/tasks/main.yml
Normal file
|
@ -0,0 +1,96 @@
|
|||
---
|
||||
|
||||
- name: Set Connexion Information for All Tasks
|
||||
set_fact:
|
||||
aws_connection_info: &aws_connection_info
|
||||
aws_access_key: "{{ aws_access_key }}"
|
||||
aws_secret_key: "{{ aws_secret_key }}"
|
||||
security_token: "{{ security_token }}"
|
||||
region: "{{ aws_region }}"
|
||||
no_log: yes
|
||||
|
||||
- block:
|
||||
- name: Create AWS Inspector Target Group
|
||||
aws_inspector_target:
|
||||
name: "{{ aws_inspector_scan_name }}"
|
||||
state: present
|
||||
tags:
|
||||
Name: "{{ aws_inspector_scan_name }}"
|
||||
changed: "no"
|
||||
<<: *aws_connection_info
|
||||
register: target_group_create
|
||||
|
||||
- name: Create AWS Inspector Target Group (Verify)
|
||||
aws_inspector_target:
|
||||
name: "{{ aws_inspector_scan_name }}"
|
||||
state: present
|
||||
tags:
|
||||
Name: "{{ aws_inspector_scan_name }}"
|
||||
changed: "no"
|
||||
<<: *aws_connection_info
|
||||
register: target_group_create_verify
|
||||
|
||||
- name: Assert Successful AWS Inspector Target Group Creation
|
||||
assert:
|
||||
that:
|
||||
- target_group_create is changed
|
||||
- target_group_create.name == aws_inspector_scan_name
|
||||
- target_group_create.tags.Name == aws_inspector_scan_name
|
||||
- target_group_create.tags.changed == "no"
|
||||
- target_group_create_verify is not changed
|
||||
- target_group_create_verify.name == aws_inspector_scan_name
|
||||
- target_group_create_verify.tags.Name == aws_inspector_scan_name
|
||||
- target_group_create_verify.tags.changed == "no"
|
||||
|
||||
- name: Change AWS Inspector Target Group Tags
|
||||
aws_inspector_target:
|
||||
name: "{{ aws_inspector_scan_name }}"
|
||||
state: present
|
||||
tags:
|
||||
Name: "{{ aws_inspector_scan_name }}"
|
||||
changed: "yes"
|
||||
<<: *aws_connection_info
|
||||
register: target_group_tag_change
|
||||
|
||||
- name: Change AWS Inspector Target Group Tags (Verify)
|
||||
aws_inspector_target:
|
||||
name: "{{ aws_inspector_scan_name }}"
|
||||
state: present
|
||||
tags:
|
||||
Name: "{{ aws_inspector_scan_name }}"
|
||||
changed: "yes"
|
||||
<<: *aws_connection_info
|
||||
register: target_group_tag_change_verify
|
||||
|
||||
- name: Assert Successful AWS Inspector Target Group Tag Change
|
||||
assert:
|
||||
that:
|
||||
- target_group_tag_change is changed
|
||||
- target_group_tag_change.name == aws_inspector_scan_name
|
||||
- target_group_tag_change.tags.Name == aws_inspector_scan_name
|
||||
- target_group_tag_change.tags.changed == "yes"
|
||||
- target_group_tag_change_verify is not changed
|
||||
- target_group_tag_change_verify.name == aws_inspector_scan_name
|
||||
- target_group_tag_change_verify.tags.Name == aws_inspector_scan_name
|
||||
- target_group_tag_change_verify.tags.changed == "yes"
|
||||
|
||||
always:
|
||||
- name: Delete AWS Inspector Target Group
|
||||
aws_inspector_target:
|
||||
name: "{{ aws_inspector_scan_name }}"
|
||||
state: absent
|
||||
<<: *aws_connection_info
|
||||
register: target_group_delete
|
||||
|
||||
- name: Delete AWS Inspector Target Group (Verify)
|
||||
aws_inspector_target:
|
||||
name: "{{ aws_inspector_scan_name }}"
|
||||
state: absent
|
||||
<<: *aws_connection_info
|
||||
register: target_group_delete_verify
|
||||
|
||||
- name: Assert Successful AWS Inspector Target Group Deletion
|
||||
assert:
|
||||
that:
|
||||
- target_group_delete is changed
|
||||
- target_group_delete_verify is not changed
|
Loading…
Reference in a new issue