diff --git a/libtaskotron.spec b/libtaskotron.spec
--- a/libtaskotron.spec
+++ b/libtaskotron.spec
@@ -4,7 +4,7 @@
 
 Name:           libtaskotron
 Version:        0.4.8
-Release:        1%{?dist}
+Release:        2%{?dist}
 Summary:        Taskotron Support Library
 
 License:        GPLv3
@@ -26,6 +26,7 @@
 Summary:        The minimal, core parts of libtaskotron
 
 Requires:       libtaskotron-config = %{version}-%{release}
+Requires:       python-jinja2 >= 2.8
 Requires:       python-progressbar >= 2.3
 Requires:       python-requests >= 2.7.0
 Requires:       python-setuptools
@@ -153,6 +154,9 @@
 %{python_sitelib}/libtaskotron/ext/disposable/*
 
 %changelog
+* Thu Jan 28 2016 Martin Krizek <mkrizek@redhat.com> - 0.4.8-2
+- Add jinja2 as a dep
+
 * Mon Jan 25 2016 Tim Flink <tflink@fedoraproject.org> - 0.4.8-1
 - Documentation updates (D726, D725)
 
diff --git a/libtaskotron/directives/create_report_directive.py b/libtaskotron/directives/create_report_directive.py
new file mode 100644
--- /dev/null
+++ b/libtaskotron/directives/create_report_directive.py
@@ -0,0 +1,118 @@
+# -*- coding: utf-8 -*-
+# Copyright 2009-2016, Red Hat, Inc.
+# License: GPL-2.0+ <http://spdx.org/licenses/GPL-2.0+>
+# See the LICENSE file for more details on Licensing
+
+from __future__ import absolute_import
+
+
+DOCUMENTATION = """
+module: create_report_directive
+short_description: generate report of task results.
+description: |
+  Generate report of task results in HTML format to given path.
+
+parameters:
+  results:
+    required: true
+    description: The output of the check in the ResultYAML format
+    type: str
+  path:
+    required: false
+    description: |
+      A path and a filename of the generated HTML report. You should always use relative
+      paths, and ``${artifactsdir}`` is considered to be the base directory. All missing
+      directories will be created automatically. Example: ``reports/overview.html``
+    type: str
+    default: report.html
+  template:
+    required: false
+    description: |
+      An absolute path to a template to be used to generate a report. Example:
+      ``/home/user/report.j2``
+    type: str
+    default: libtaskotron/libtaskotron/report_templates/html.j2
+raises: |
+  * :class:`.TaskotronDirectiveError`: if ``results`` is not in a valid YAML
+    format or if given report path cannot be created.
+version_added: 0.4
+"""
+
+EXAMPLES = """
+These two actions first run ``run_rpmlint.py`` and export its YAML output to
+``${rpmlint_output}``, and then feed this YAML output to the ``create_report``
+directive::
+
+    - name: run rpmlint on downloaded rpms
+      python:
+          file: run_rpmlint.py
+          callable: run
+          workdir: ${workdir}
+      export: rpmlint_output
+
+    - name: make html report of the results
+      create_report:
+          results: ${rpmlint_output}
+"""
+
+import os
+
+import jinja2
+
+from libtaskotron.directives import BaseDirective
+
+from libtaskotron import check
+from libtaskotron import file_utils
+from libtaskotron.exceptions import TaskotronDirectiveError, TaskotronValueError
+from libtaskotron.logger import log
+
+
+directive_class = 'CreateReportDirective'
+
+TEMPL_DIR = 'libtaskotron/report_templates'
+
+
+class CreateReportDirective(BaseDirective):
+
+    def process(self, input_data, env_data):
+        try:
+            check_details = check.import_YAML(input_data['results'])
+            log.debug("YAML output parsed OK.")
+        except TaskotronValueError as e:
+            raise TaskotronDirectiveError("Failed to load 'results': %s" % e.message)
+
+        log.debug('Generating report of %s results...', len(check_details))
+
+        results = []
+        for detail in check_details:
+            results.append({
+                'checkname': detail.checkname or env_data['checkname'],
+                'outcome': detail.outcome,
+                'note': detail.note or '---',
+                'item': detail.item,
+                'type': detail.report_type,
+                'artifact': os.path.basename(detail.artifact),
+            })
+
+        if 'path' in input_data:
+            report_fname = os.path.join(env_data['artifactsdir'], input_data['path'])
+        else:
+            report_fname = os.path.join(env_data['artifactsdir'], 'report.html')
+
+        if 'template' in input_data:
+            template_fname = input_data['template']
+        else:
+            template_fname = os.path.join(TEMPL_DIR, 'html.j2')
+
+        try:
+            file_utils.makedirs(os.path.dirname(report_fname))
+        except OSError, e:
+            raise TaskotronDirectiveError(e)
+
+        with open(template_fname) as f_template:
+            with open(report_fname, 'w') as f_report:
+                template = jinja2.Template(f_template.read())
+                report = template.render(results=results, artifactsdir=env_data['artifactsdir'])
+                f_report.write(report)
+
+        log.info('Report generated in: %s', os.path.abspath(report_fname))
diff --git a/libtaskotron/report_templates/html.j2 b/libtaskotron/report_templates/html.j2
new file mode 100644
--- /dev/null
+++ b/libtaskotron/report_templates/html.j2
@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Taskotron task results</title>
+<style>
+body {
+    font-family: Arial, sans-serif;
+}
+a {
+    color: #3c6eb4;
+    text-decoration: none;
+    line-height: inherit;
+}
+a:hover {
+    color: #3662a1;
+}
+h1 {
+    border-bottom: solid 1px #e6e6e6;
+}
+.failed {
+    font-weight: bold;
+    color: red;
+}
+.passed {
+    font-weight: bold;
+    color: green;
+}
+.other_result {
+    font-weight: bold;
+    color: yellow;
+}
+table.results {
+    width: 100%;
+    text-align: left;
+    background: #f2f2f2;
+    border: solid 1px #e6e6e6;
+    margin: 0 0 22px 0;
+    padding: 5px;
+
+}
+.results th {
+    padding: 0 5px 0 0;
+}
+.results td {
+    border-top: solid 1px #e6e6e6;
+    padding: 3px 10px 3px 0;
+}
+</style>
+</head>
+<body>
+<h1>Taskotron task results</h1>
+<p>
+    <strong>Artifacts directory:</strong> <a href="file://{{ artifactsdir }}">{{ artifactsdir }}</a>
+</p>
+<table class="results">
+    <thead>
+        <tr>
+            <th>Task</th>
+            <th>Item</th>
+            <th>Outcome</th>
+            <th>Artifact</th>
+            <th>Note</th>
+        </tr>
+    </thead>
+    <tbody>
+    {% for result in results %}
+        <tr>
+            <td>{{ result.checkname }}</td>
+            <td>
+            {% if result.type == "koji_build" %}
+            <a href="http://koji.fedoraproject.org/koji/search?match=glob&type=build&terms={{ result.item }}">{{ result.item }}</a>
+            {% elif result.type == "bodhi_update" %}
+            <a href="https://bodhi.fedoraproject.org/updates/{{ result.item }}">{{ result.item }}</a>
+            {% else %}
+            {{ result.item }}
+            {% endif %}
+            ({{ result.type }})
+            </td>
+            <td class="{% if result.outcome == 'FAILED' %}failed{% elif result.outcome == 'PASSED' %}passed{% else %}other_result{% endif %}">{{ result.outcome }}</td>
+            <td><a href="file://{{ artifactsdir }}/{{ result.artifact }}">{{ result.artifact }}</a></td>
+            <td>{{ result.note }}</td>
+        </tr>
+    {% endfor %}
+    </tbody>
+</table>
+</body>
+</html>
diff --git a/requirements.txt b/requirements.txt
--- a/requirements.txt
+++ b/requirements.txt
@@ -11,6 +11,7 @@
 # you can define a dependency range here instead of an exact version.
 
 dingus == 0.3.4
+Jinja2 == 2.8
 munch == 2.0.2
 mock == 1.0.1
 paramiko == 1.15.1
diff --git a/testing/test_create_report_directive.py b/testing/test_create_report_directive.py
new file mode 100644
--- /dev/null
+++ b/testing/test_create_report_directive.py
@@ -0,0 +1,48 @@
+# -*- coding: utf-8 -*-
+# Copyright 2009-2016, Red Hat, Inc.
+# License: GPL-2.0+ <http://spdx.org/licenses/GPL-2.0+>
+# See the LICENSE file for more details on Licensing
+
+import pytest
+import mock
+
+from libtaskotron.directives import create_report_directive
+from libtaskotron.exceptions import TaskotronDirectiveError
+
+from libtaskotron import check
+
+
+@pytest.mark.usefixtures('setup')
+class TestCreateReportDirective():
+
+    @pytest.fixture
+    def setup(self):
+        self.cd = check.CheckDetail(
+            item='foo_bar',
+            report_type=check.ReportType.KOJI_BUILD,
+            outcome='NEEDS_INSPECTION',
+            note='foo_bar note',
+            output=["foo\nbar"],
+            keyvals={"foo": "moo1", "bar": "moo2"},
+            artifact='artifact.log',
+        )
+
+        self.yaml = check.export_YAML(self.cd)
+
+        self.ref_input = {'results': self.yaml}
+        self.ref_envdata = {
+            'resultsdb_job_id': 1,
+            'checkname': 'test_resultsdb_report',
+            'jobid': 'all/123',
+            'uuid': 'c25237a4-b6b3-11e4-b98a-3c970e018701',
+            'artifactsdir': '/some/directory/',
+        }
+
+        self.rd = create_report_directive.CreateReportDirective()
+
+    def test_failed_yaml_import(self, monkeypatch):
+        import_YAML = mock.Mock(side_effect=(TaskotronDirectiveError('Testing Error')))
+        monkeypatch.setattr(check, 'import_YAML', import_YAML)
+
+        with pytest.raises(TaskotronDirectiveError):
+            self.rd.process(self.ref_input, self.ref_envdata)

