diff --git a/doc/ref/states/all/salt.states.debconfmod.rst b/doc/ref/states/all/salt.states.debconfmod.rst
new file mode 100644
index 000000000000..cebd59773f69
--- /dev/null
+++ b/doc/ref/states/all/salt.states.debconfmod.rst
@@ -0,0 +1,5 @@
+salt.states.debconfmod
+======================
+
+.. automodule:: salt.states.debconfmod
+    :members:
diff --git a/salt/states/debconfmod.py b/salt/states/debconfmod.py
new file mode 100644
index 000000000000..4d399408341c
--- /dev/null
+++ b/salt/states/debconfmod.py
@@ -0,0 +1,220 @@
+"""
+Management of debconf selections
+================================
+
+:depends:   - debconf-utils package
+
+The debconfmod state module manages the enforcement of debconf selections,
+this state can set those selections prior to package installation.
+
+Available Functions
+-------------------
+
+The debconfmod state has two functions, the ``set`` and ``set_file`` functions
+
+set
+    Set debconf selections from the state itself
+
+set_file
+    Set debconf selections from a file
+
+.. code-block:: yaml
+
+    nullmailer-debconf:
+      debconf.set:
+        - name: nullmailer
+        - data:
+            'shared/mailname': {'type': 'string', 'value': 'server.domain.tld'}
+            'nullmailer/relayhost': {'type': 'string', 'value': 'mail.domain.tld'}
+
+    ferm-debconf:
+      debconf.set:
+        - name: ferm
+        - data:
+            'ferm/enable': {'type': 'boolean', 'value': True}
+
+.. note::
+    Due to how PyYAML imports nested dicts (see :ref:`here <yaml-idiosyncrasies>`),
+    the values in the ``data`` dict must be indented four spaces instead of two.
+
+If you're setting debconf values that requires `dpkg-reconfigure`, you can use
+the ``onchanges`` requisite to reconfigure your package:
+
+.. code-block:: yaml
+
+    set-default-shell:
+      debconf.set:
+        - name: dash
+        - data:
+            'dash/sh': {'type': 'boolean', 'value': false}
+
+    reconfigure-dash:
+      cmd.run:
+        - name: dpkg-reconfigure -f noninteractive dash
+        - onchanges:
+          - debconf: set-default-shell
+
+Every time the ``set-default-shell`` state changes, the ``reconfigure-dash``
+state will also run.
+
+.. note::
+    For boolean types, the value should be ``true`` or ``false``, not
+    ``'true'`` or ``'false'``.
+"""
+
+# Define the module's virtual name
+__virtualname__ = "debconf"
+
+
+def __virtual__():
+    """
+    Confirm this module is on a Debian based system
+    """
+    if __grains__["os_family"] != "Debian":
+        return (False, "debconf state only runs on Debian systems")
+    # Check that debconf was loaded
+    if "debconf.show" not in __salt__:
+        return (False, "debconf module could not be loaded")
+
+    return __virtualname__
+
+
+def set_file(name, source, template=None, context=None, defaults=None, **kwargs):
+    """
+    Set debconf selections from a file or a template
+
+    .. code-block:: yaml
+
+        <state_id>:
+          debconf.set_file:
+            - source: salt://pathto/pkg.selections
+
+        <state_id>:
+          debconf.set_file:
+            - source: salt://pathto/pkg.selections?saltenv=myenvironment
+
+        <state_id>:
+          debconf.set_file:
+            - source: salt://pathto/pkg.selections.jinja2
+            - template: jinja
+            - context:
+                some_value: "false"
+
+    source:
+        The location of the file containing the package selections
+
+    template
+        If this setting is applied then the named templating engine will be
+        used to render the package selections file, currently jinja, mako, and
+        wempy are supported
+
+    context
+        Overrides default context variables passed to the template.
+
+    defaults
+        Default context passed to the template.
+    """
+    ret = {"name": name, "changes": {}, "result": True, "comment": ""}
+
+    if context is None:
+        context = {}
+    elif not isinstance(context, dict):
+        ret["result"] = False
+        ret["comment"] = "Context must be formed as a dict"
+        return ret
+
+    if defaults is None:
+        defaults = {}
+    elif not isinstance(defaults, dict):
+        ret["result"] = False
+        ret["comment"] = "Defaults must be formed as a dict"
+        return ret
+
+    if __opts__["test"]:
+        ret["result"] = None
+        ret["comment"] = "Debconf selections would have been set."
+        return ret
+
+    if template:
+        result = __salt__["debconf.set_template"](
+            source, template, context, defaults, **kwargs
+        )
+    else:
+        result = __salt__["debconf.set_file"](source, **kwargs)
+
+    if result:
+        ret["comment"] = "Debconf selections were set."
+    else:
+        ret["result"] = False
+        ret["comment"] = "Unable to set debconf selections from file."
+
+    return ret
+
+
+def set(name, data, **kwargs):
+    """
+    Set debconf selections
+
+    .. code-block:: yaml
+
+        <state_id>:
+          debconf.set:
+            - name: <name>
+            - data:
+                <question>: {'type': <type>, 'value': <value>}
+                <question>: {'type': <type>, 'value': <value>}
+
+    name:
+        The package name to set answers for.
+
+    data:
+        A set of questions/answers for debconf. Note that everything under
+        this must be indented twice.
+
+    question:
+        The question the is being pre-answered
+
+    type:
+        The type of question that is being asked (string, boolean, select, etc.)
+
+    value:
+        The answer to the question
+    """
+    ret = {"name": name, "changes": {}, "result": True, "comment": ""}
+
+    current = __salt__["debconf.show"](name)
+
+    for key, args in data.items():
+        # For debconf data, valid booleans are 'true' and 'false';
+        # But str()'ing the args['value'] will result in 'True' and 'False'
+        # which will be ignored and overridden by a dpkg-reconfigure.
+
+        # So we should manually set these values to lowercase ones,
+        # before any str() call is performed.
+
+        if args["type"] == "boolean":
+            args["value"] = "true" if args["value"] else "false"
+
+        if current is not None and [key, args["type"], str(args["value"])] in current:
+            if ret["comment"] == "":
+                ret["comment"] = "Unchanged answers: "
+            ret["comment"] += f"{key} "
+        else:
+            if __opts__["test"]:
+                ret["result"] = None
+                ret["changes"][key] = "New value: {}".format(args["value"])
+            else:
+                if __salt__["debconf.set"](name, key, args["type"], args["value"]):
+                    if args["type"] == "password":
+                        ret["changes"][key] = "(password hidden)"
+                    else:
+                        ret["changes"][key] = "{}".format(args["value"])
+                else:
+                    ret["result"] = False
+                    ret["comment"] = "Some settings failed to be applied."
+                    ret["changes"][key] = "Failed to set!"
+
+    if not ret["changes"]:
+        ret["comment"] = "All specified answers are already set"
+
+    return ret
diff --git a/tests/pytests/unit/states/test_debconfmod.py b/tests/pytests/unit/states/test_debconfmod.py
new file mode 100644
index 000000000000..3151e84275f6
--- /dev/null
+++ b/tests/pytests/unit/states/test_debconfmod.py
@@ -0,0 +1,67 @@
+"""
+    :codeauthor: Jayesh Kariya <jayeshk@saltstack.com>
+"""
+
+import pytest
+
+import salt.states.debconfmod as debconfmod
+from tests.support.mock import MagicMock, patch
+
+
+@pytest.fixture
+def configure_loader_modules():
+    return {debconfmod: {}}
+
+
+def test_set_file():
+    """
+    Test to set debconf selections from a file or a template
+    """
+    name = "nullmailer"
+    source = "salt://pathto/pkg.selections"
+
+    ret = {"name": name, "result": False, "comment": "", "changes": {}}
+
+    comt = "Context must be formed as a dict"
+    ret.update({"comment": comt})
+    assert debconfmod.set_file(name, source, context="salt") == ret
+
+    comt = "Defaults must be formed as a dict"
+    ret.update({"comment": comt})
+    assert debconfmod.set_file(name, source, defaults="salt") == ret
+
+    with patch.dict(debconfmod.__opts__, {"test": True}):
+        comt = "Debconf selections would have been set."
+        ret.update({"comment": comt, "result": None})
+        assert debconfmod.set_file(name, source) == ret
+
+        with patch.dict(debconfmod.__opts__, {"test": False}):
+            mock = MagicMock(return_value=True)
+            with patch.dict(debconfmod.__salt__, {"debconf.set_file": mock}):
+                comt = "Debconf selections were set."
+                ret.update({"comment": comt, "result": True})
+                assert debconfmod.set_file(name, source) == ret
+
+
+def test_set():
+    """
+    Test to set debconf selections
+    """
+    name = "nullmailer"
+    data = {
+        "shared/mailname": {"type": "string", "value": "server.domain.tld"},
+        "nullmailer/relayhost": {"type": "string", "value": "mail.domain.tld"},
+    }
+
+    ret = {"name": name, "result": None, "comment": "", "changes": {}}
+
+    changes = {
+        "nullmailer/relayhost": "New value: mail.domain.tld",
+        "shared/mailname": "New value: server.domain.tld",
+    }
+
+    mock = MagicMock(return_value=None)
+    with patch.dict(debconfmod.__salt__, {"debconf.show": mock}):
+        with patch.dict(debconfmod.__opts__, {"test": True}):
+            ret.update({"changes": changes})
+            assert debconfmod.set(name, data) == ret