Skip to content

Commit

Permalink
ssm_inventory_info - new module to retrieve ssm inventory for configu…
Browse files Browse the repository at this point in the history
…red ec2 instances (#1745)

ssm_inventory_info module

SUMMARY

new module to retrieve ssm inventory info for EC2 configured instances

ISSUE TYPE


New Module Pull Request

COMPONENT NAME

ssm_inventory_info

Reviewed-by: Mark Chappell
  • Loading branch information
abikouo authored Mar 7, 2023
1 parent 1debf21 commit ad89e49
Show file tree
Hide file tree
Showing 3 changed files with 266 additions and 0 deletions.
1 change: 1 addition & 0 deletions meta/runtime.yml
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ action_groups:
- sns_topic
- sns_topic_info
- sqs_queue
- ssm_inventory_info
- ssm_parameter
- stepfunctions_state_machine
- stepfunctions_state_machine_execution
Expand Down
120 changes: 120 additions & 0 deletions plugins/modules/ssm_inventory_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright: Contributors to the Ansible project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

DOCUMENTATION = """
module: ssm_inventory_info
version_added: 6.0.0
short_description: Get SSM inventory information for EC2 instance
description:
- Gather SSM inventory for EC2 instance configured with SSM.
author: 'Aubin Bikouo (@abikouo)'
options:
instance_id:
description:
- EC2 instance id.
required: true
type: str
extends_documentation_fragment:
- amazon.aws.common.modules
- amazon.aws.region.modules
- amazon.aws.boto3
"""

EXAMPLES = """
- name: Retrieve SSM inventory info for instance id 'i-012345678902'
community.aws.ssm_inventory_info:
instance_id: 'i-012345678902'
"""


RETURN = """
ssm_inventory:
returned: on success
description: >
SSM inventory information.
type: dict
sample: {
'agent_type': 'amazon-ssm-agent',
'agent_version': '3.2.582.0',
'computer_name': 'ip-172-31-44-166.ec2.internal',
'instance_id': 'i-039eb9b1f55934ab6',
'instance_status': 'Active',
'ip_address': '172.31.44.166',
'platform_name': 'Fedora Linux',
'platform_type': 'Linux',
'platform_version': '37',
'resource_type': 'EC2Instance'
}
"""


try:
import botocore
except ImportError:
pass # Handled by AnsibleAWSModule

from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict

from ansible_collections.community.aws.plugins.module_utils.modules import AnsibleCommunityAWSModule as AnsibleAWSModule


class SsmInventoryInfoFailure(Exception):
def __init__(self, exc, msg):
self.exc = exc
self.msg = msg
super().__init__(self)


def get_ssm_inventory(connection, filters):
try:
return connection.get_inventory(Filters=filters)
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
raise SsmInventoryInfoFailure(exc=e, msg="get_ssm_inventory() failed.")


def execute_module(module, connection):

instance_id = module.params.get("instance_id")
try:
filters = [
{
"Key": "AWS:InstanceInformation.InstanceId",
"Values": [instance_id]
}
]

response = get_ssm_inventory(connection, filters)
entities = response.get("Entities", [])
ssm_inventory = {}
if entities:
content = entities[0].get("Data", {}).get("AWS:InstanceInformation", {}).get("Content", [])
if content:
ssm_inventory = camel_dict_to_snake_dict(content[0])
module.exit_json(changed=False, ssm_inventory=ssm_inventory)
except SsmInventoryInfoFailure as e:
module.fail_json_aws(exception=e.exc, msg=e.msg)


def main():
argument_spec = dict(
instance_id=dict(required=True, type="str"),
)

module = AnsibleAWSModule(
argument_spec=argument_spec,
supports_check_mode=True,
)

connection = module.client("ssm")
execute_module(module, connection)


if __name__ == "__main__":
main()
145 changes: 145 additions & 0 deletions tests/unit/plugins/modules/test_ssm_inventory_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# -*- coding: utf-8 -*-

# Copyright: Contributors to the Ansible project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

import pytest
from botocore.exceptions import BotoCoreError

from unittest.mock import MagicMock, patch
from ansible_collections.community.aws.plugins.modules.ssm_inventory_info import (
execute_module,
get_ssm_inventory,
SsmInventoryInfoFailure,
)


def test_get_ssm_inventory():
connection = MagicMock()
inventory_response = MagicMock()
connection.get_inventory.return_value = inventory_response
filters = MagicMock()

assert get_ssm_inventory(connection, filters) == inventory_response
connection.get_inventory.assert_called_once_with(
Filters=filters
)


def test_get_ssm_inventory_failure():
connection = MagicMock()
connection.get_inventory.side_effect = BotoCoreError(error="failed", operation="get_ssm_inventory")
filters = MagicMock()

with pytest.raises(SsmInventoryInfoFailure):
get_ssm_inventory(connection, filters)


@patch('ansible_collections.community.aws.plugins.modules.ssm_inventory_info.get_ssm_inventory')
def test_execute_module(m_get_ssm_inventory):

instance_id = "i-0202020202020202"
aws_inventory = {
'AgentType': 'amazon-ssm-agent',
'AgentVersion': '3.2.582.0',
'ComputerName': 'ip-172-31-44-166.ec2.internal',
'InstanceId': 'i-039eb9b1f55934ab6',
'InstanceStatus': 'Active',
'IpAddress': '172.31.44.166',
'PlatformName': 'Fedora Linux',
'PlatformType': 'Linux',
'PlatformVersion': '37',
'ResourceType': 'EC2Instance'
}

ansible_inventory = {
'agent_type': 'amazon-ssm-agent',
'agent_version': '3.2.582.0',
'computer_name': 'ip-172-31-44-166.ec2.internal',
'instance_id': 'i-039eb9b1f55934ab6',
'instance_status': 'Active',
'ip_address': '172.31.44.166',
'platform_name': 'Fedora Linux',
'platform_type': 'Linux',
'platform_version': '37',
'resource_type': 'EC2Instance'
}

m_get_ssm_inventory.return_value = {
"Entities": [
{
'Id': instance_id,
"Data": {
"AWS:InstanceInformation": {"Content": [aws_inventory]}
}
}
],
"Status": 200
}

connection = MagicMock()
module = MagicMock()
module.params = dict(
instance_id=instance_id
)
module.exit_json.side_effect = SystemExit(1)
module.fail_json_aws.side_effect = SystemError(2)

with pytest.raises(SystemExit):
execute_module(module, connection)

module.exit_json.assert_called_once_with(
changed=False, ssm_inventory=ansible_inventory
)


@patch('ansible_collections.community.aws.plugins.modules.ssm_inventory_info.get_ssm_inventory')
def test_execute_module_no_data(m_get_ssm_inventory):

instance_id = "i-0202020202020202"

m_get_ssm_inventory.return_value = {
"Entities": [
{
'Id': instance_id,
"Data": {}
}
],
}

connection = MagicMock()
module = MagicMock()
module.params = dict(
instance_id=instance_id
)
module.exit_json.side_effect = SystemExit(1)
module.fail_json_aws.side_effect = SystemError(2)

with pytest.raises(SystemExit):
execute_module(module, connection)

module.exit_json.assert_called_once_with(
changed=False, ssm_inventory={}
)


@patch('ansible_collections.community.aws.plugins.modules.ssm_inventory_info.get_ssm_inventory')
def test_execute_module_failure(m_get_ssm_inventory):

instance_id = "i-0202020202020202"

m_get_ssm_inventory.side_effect = SsmInventoryInfoFailure(
exc=BotoCoreError(error="failed", operation="get_ssm_inventory"),
msg="get_ssm_inventory() failed."
)

connection = MagicMock()
module = MagicMock()
module.params = dict(
instance_id=instance_id
)
module.exit_json.side_effect = SystemExit(1)
module.fail_json_aws.side_effect = SystemError(2)

with pytest.raises(SystemError):
execute_module(module, connection)

0 comments on commit ad89e49

Please sign in to comment.