diff --git a/changelogs/fragments/20240129-aws_ec2-inventory-bugfix.yml b/changelogs/fragments/20240129-aws_ec2-inventory-bugfix.yml new file mode 100644 index 00000000000..c197672ad36 --- /dev/null +++ b/changelogs/fragments/20240129-aws_ec2-inventory-bugfix.yml @@ -0,0 +1,3 @@ +--- +bugfixes: + - plugins/inventory/aws_ec2 - Fix failure when retrieving information for more than 40 instances with use_ssm_inventory (https://github.com/ansible-collections/amazon.aws/issues/1713). diff --git a/plugins/inventory/aws_ec2.py b/plugins/inventory/aws_ec2.py index d0c6f49ed5a..8b9796b7f19 100644 --- a/plugins/inventory/aws_ec2.py +++ b/plugins/inventory/aws_ec2.py @@ -659,8 +659,8 @@ def _query(self, regions, include_filters, exclude_filters, strict_permissions, return {"aws_ec2": instances} def _add_ssm_information(self, connection, instances): - filters = [{"Key": "AWS:InstanceInformation.InstanceId", "Values": [x["InstanceId"] for x in instances]}] - result = _get_ssm_information(connection, filters) + instance_ids = [x["InstanceId"] for x in instances] + result = self._get_multiple_ssm_inventories(connection, instance_ids) for entity in result.get("Entities", []): for x in instances: if x["InstanceId"] == entity["Id"]: @@ -669,6 +669,19 @@ def _add_ssm_information(self, connection, instances): x["SsmInventory"] = content[0] break + def _get_multiple_ssm_inventories(self, connection, instance_ids): + result = {} + # SSM inventory filters Values list can contain a maximum of 40 items so we need to retrieve 40 at a time + # https://docs.aws.amazon.com/systems-manager/latest/APIReference/API_InventoryFilter.html + while len(instance_ids) > 40: + filters = [{"Key": "AWS:InstanceInformation.InstanceId", "Values": instance_ids[:40]}] + result.update(_get_ssm_information(connection, filters)) + instance_ids = instance_ids[40:] + if instance_ids: + filters = [{"Key": "AWS:InstanceInformation.InstanceId", "Values": instance_ids}] + result.update(_get_ssm_information(connection, filters)) + return result + def _populate( self, groups, diff --git a/tests/unit/plugins/inventory/test_aws_ec2.py b/tests/unit/plugins/inventory/test_aws_ec2.py index d136880c3b8..8cced16621e 100644 --- a/tests/unit/plugins/inventory/test_aws_ec2.py +++ b/tests/unit/plugins/inventory/test_aws_ec2.py @@ -651,3 +651,39 @@ def test_inventory__add_ssm_information(m_get_ssm_information, inventory): filters = [{"Key": "AWS:InstanceInformation.InstanceId", "Values": [x["InstanceId"] for x in instances]}] m_get_ssm_information.assert_called_once_with(connection, filters) + + +@patch("ansible_collections.amazon.aws.plugins.inventory.aws_ec2._get_ssm_information") +def test_inventory__get_multiple_ssm_inventories(m_get_ssm_information, inventory): + instances = [{"InstanceId": f"i-00{i}", "Name": f"instance {i}"} for i in range(41)] + result = { + "StatusCode": 200, + "Entities": [ + { + "Id": f"i-00{i}", + "Data": { + "AWS:InstanceInformation": { + "Content": [{"os_type": "Linux", "os_name": "Fedora", "os_version": 37}] + } + }, + } + for i in range(41) + ], + } + m_get_ssm_information.return_value = result + + connection = MagicMock() + + expected = [ + { + "InstanceId": f"i-00{i}", + "Name": f"instance {i}", + "SsmInventory": {"os_type": "Linux", "os_name": "Fedora", "os_version": 37}, + } + for i in range(41) + ] + + inventory._add_ssm_information(connection, instances) + assert expected == instances + + assert 2 == m_get_ssm_information.call_count