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:
Dennis Conrad 2018-05-01 21:04:03 +01:00 committed by ansibot
parent 4134f1204b
commit 77f5a8f422
4 changed files with 347 additions and 0 deletions

View 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()

View file

@ -0,0 +1,2 @@
cloud/aws
posix/ci/cloud/group3/aws

View file

@ -0,0 +1,3 @@
---
aws_inspector_scan_name: "aws_inspector_scan-{{ ansible_date_time.epoch }}"

View 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