# Buggy code
def get_stack_events(cfn, stack_name, token_filter=None):
    """This event data was never correct, it worked as a side effect. So the v2.3 format is different."""
    ret = {"events": [], "log": []}

    try:
        pg = cfn.get_paginator("describe_stack_events").paginate(StackName=stack_name)
        if token_filter is not None:
            events = list(
                pg.search(
                    "StackEvents[?ClientRequestToken == '{0}']".format(token_filter)
                )
            )
        else:
            events = list(pg)
    except (
        botocore.exceptions.ValidationError,
        botocore.exceptions.ClientError,
    ) as err:
        error_msg = boto_exception(err)
        if "does not exist" in error_msg:
            # missing stack, don't bail.
            ret["log"].append("Stack does not exist.")
            return ret
        ret["log"].append("Unknown error: " + str(error_msg))
        return ret

    for e in events:
        eventline = (
            "StackEvent {ResourceType} {LogicalResourceId} {ResourceStatus}".format(**e)
        )
        ret["events"].append(eventline)

        if e["ResourceStatus"].endswith("FAILED"):
            failline = "{ResourceType} {LogicalResourceId} {ResourceStatus}: {ResourceStatusReason}".format(
                **e
            )
            ret["log"].append(failline)

    return ret


# Traceback (KeyError)
The full traceback is:
Traceback (most recent call last):
File "/tmp/ansible_w3U9Kn/ansible_module_cloudformation.py", line 329, in create_changeset
result = stack_operation(cfn, stack_params['StackName'], 'CREATE_CHANGESET')
File "/tmp/ansible_w3U9Kn/ansible_module_cloudformation.py", line 402, in stack_operation
ret = get_stack_events(cfn, stack_name, op_token)
File "/tmp/ansible_w3U9Kn/ansible_module_cloudformation.py", line 270, in get_stack_events
eventline = 'StackEvent {ResourceType} {LogicalResourceId} {ResourceStatus}'.format(**e)
KeyError: 'ResourceType'

fatal: [localhost]: FAILED! => {
"changed": false,
"invocation": {
"module_args": {
"aws_access_key": null,
"aws_secret_key": null,
"changeset_name": "New change set",
"create_changeset": true,
"disable_rollback": false,
"ec2_url": null,
"notification_arns": null,
"profile": null,
"region": "us-east-1",
"role_arn": null,
"security_token": null,
"stack_name": "Test1",
"stack_policy": null,
"state": "present",
"tags": {
...
},
"template": null,
"template_format": null,
"template_parameters": {},
"template_url": "...",
"termination_protection": null,
"validate_certs": true
}
},
"msg": "Failed to create change set: ResourceType 'ResourceType' - <type 'exceptions.KeyError'>"
}

# Fixed code
def get_stack_events(cfn, stack_name, token_filter=None):
    """This event data was never correct, it worked as a side effect. So the v2.3 format is different."""
    ret = {"events": [], "log": []}

    try:
        pg = cfn.get_paginator("describe_stack_events").paginate(StackName=stack_name)
        if token_filter is not None:
            events = list(
                pg.search(
                    "StackEvents[?ClientRequestToken == '{0}']".format(token_filter)
                )
            )
        else:
            events = list(pg.search("StackEvents[*]"))
    except (
        botocore.exceptions.ValidationError,
        botocore.exceptions.ClientError,
    ) as err:
        error_msg = boto_exception(err)
        if "does not exist" in error_msg:
            # missing stack, don't bail.
            ret["log"].append("Stack does not exist.")
            return ret
        ret["log"].append("Unknown error: " + str(error_msg))
        return ret

    for e in events:
        eventline = (
            "StackEvent {ResourceType} {LogicalResourceId} {ResourceStatus}".format(**e)
        )
        ret["events"].append(eventline)

        if e["ResourceStatus"].endswith("FAILED"):
            failline = "{ResourceType} {LogicalResourceId} {ResourceStatus}: {ResourceStatusReason}".format(
                **e
            )
            ret["log"].append(failline)

    return ret