mirror of https://github.com/ansible/ansible.git
Add AWS Inspector Target Module (#37464)
* Add AWS Inspector Target Module * "ansible-test sanity" Fixes * * Rename module * Add integration test * Incorporate feedback from s-hertelpull/39572/head
parent
4134f1204b
commit
77f5a8f422
@ -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()
|
@ -0,0 +1,2 @@
|
|||||||
|
cloud/aws
|
||||||
|
posix/ci/cloud/group3/aws
|
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
aws_inspector_scan_name: "aws_inspector_scan-{{ ansible_date_time.epoch }}"
|
@ -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 New Issue