# Buggy code
def update_stack(module, stack_params, cfn):
    if "TemplateBody" not in stack_params and "TemplateURL" not in stack_params:
        stack_params["UsePreviousTemplate"] = True

    # if the state is present and the stack already exists, we try to update it.
    # AWS will tell us if the stack template and parameters are the same and
    # don't need to be updated.
    try:
        cfn.update_stack(**stack_params)
        result = stack_operation(
            cfn, stack_params["StackName"], "UPDATE", stack_params["ClientRequestToken"]
        )
    except Exception as err:
        error_msg = boto_exception(err)
        if "No updates are to be performed." in error_msg:
            result = dict(changed=False, output="Stack is already up-to-date.")
        else:
            module.fail_json(
                msg="Failed to update stack {0}: {1}".format(
                    stack_params.get("StackName"), error_msg
                ),
                exception=traceback.format_exc(),
            )
    if not result:
        module.fail_json(msg="empty result")
    return result


# 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 update_stack(module, stack_params, cfn):
    if "TemplateBody" not in stack_params and "TemplateURL" not in stack_params:
        stack_params["UsePreviousTemplate"] = True

    # if the state is present and the stack already exists, we try to update it.
    # AWS will tell us if the stack template and parameters are the same and
    # don't need to be updated.
    try:
        cfn.update_stack(**stack_params)
        result = stack_operation(
            cfn,
            stack_params["StackName"],
            "UPDATE",
            stack_params.get("ClientRequestToken", None),
        )
    except Exception as err:
        error_msg = boto_exception(err)
        if "No updates are to be performed." in error_msg:
            result = dict(changed=False, output="Stack is already up-to-date.")
        else:
            module.fail_json(
                msg="Failed to update stack {0}: {1}".format(
                    stack_params.get("StackName"), error_msg
                ),
                exception=traceback.format_exc(),
            )
    if not result:
        module.fail_json(msg="empty result")
    return result