# Buggy code
def create_stack(module, stack_params, cfn):
    if "TemplateBody" not in stack_params and "TemplateURL" not in stack_params:
        module.fail_json(
            msg="Either 'template' or 'template_url' is required when the stack does not exist."
        )

    # 'disablerollback' and 'EnableTerminationProtection' only
    # apply on creation, not update.
    stack_params["DisableRollback"] = module.params["disable_rollback"]
    if module.params.get("termination_protection") is not None:
        if boto_supports_termination_protection(cfn):
            stack_params["EnableTerminationProtection"] = bool(
                module.params.get("termination_protection")
            )
        else:
            module.fail_json(
                msg="termination_protection parameter requires botocore >= 1.7.18"
            )

    try:
        cfn.create_stack(**stack_params)
        result = stack_operation(
            cfn, stack_params["StackName"], "CREATE", stack_params["ClientRequestToken"]
        )
    except Exception as err:
        error_msg = boto_exception(err)
        module.fail_json(
            msg="Failed to create 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 create_stack(module, stack_params, cfn):
    if "TemplateBody" not in stack_params and "TemplateURL" not in stack_params:
        module.fail_json(
            msg="Either 'template' or 'template_url' is required when the stack does not exist."
        )

    # 'disablerollback' and 'EnableTerminationProtection' only
    # apply on creation, not update.
    stack_params["DisableRollback"] = module.params["disable_rollback"]
    if module.params.get("termination_protection") is not None:
        if boto_supports_termination_protection(cfn):
            stack_params["EnableTerminationProtection"] = bool(
                module.params.get("termination_protection")
            )
        else:
            module.fail_json(
                msg="termination_protection parameter requires botocore >= 1.7.18"
            )

    try:
        cfn.create_stack(**stack_params)
        result = stack_operation(
            cfn,
            stack_params["StackName"],
            "CREATE",
            stack_params.get("ClientRequestToken", None),
        )
    except Exception as err:
        error_msg = boto_exception(err)
        module.fail_json(
            msg="Failed to create 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