diff --git a/sysutils/puppet-agent/Makefile b/sysutils/puppet-agent/Makefile
new file mode 100644
index 000000000..af7209fe7
--- /dev/null
+++ b/sysutils/puppet-agent/Makefile
@@ -0,0 +1,8 @@
+PLUGIN_NAME= puppet-agent
+PLUGIN_VERSION= 0.1
+PLUGIN_DEVEL= yes
+PLUGIN_COMMENT= Manage Puppet Agent
+PLUGIN_DEPENDS= puppet7
+PLUGIN_MAINTAINER= jan.wink93@gmail.com
+
+.include "../../Mk/plugins.mk"
diff --git a/sysutils/puppet-agent/pkg-descr b/sysutils/puppet-agent/pkg-descr
new file mode 100644
index 000000000..ed7736616
--- /dev/null
+++ b/sysutils/puppet-agent/pkg-descr
@@ -0,0 +1,7 @@
+Puppet lets you centrally manage every important aspect of your system using
+a cross-platform specification language that manages all the separate
+elements normally aggregated in different files, like users, cron jobs, and
+hosts, along with obviously discrete elements like packages, services, and
+files.
+
+WWW: https://puppet.com/docs/puppet/latest/man/agent.html
diff --git a/sysutils/puppet-agent/src/etc/inc/plugins.inc.d/puppetagent.inc b/sysutils/puppet-agent/src/etc/inc/plugins.inc.d/puppetagent.inc
new file mode 100644
index 000000000..bbeb217b0
--- /dev/null
+++ b/sysutils/puppet-agent/src/etc/inc/plugins.inc.d/puppetagent.inc
@@ -0,0 +1,61 @@
+ gettext('Puppet Agent Service'),
+ 'pidfile' => '/var/run/puppet/agent.pid',
+ 'configd' => array(
+ 'restart' => array('puppetagent restart'),
+ 'start' => array('puppetagent start'),
+ 'stop' => array('puppetagent stop'),
+ ),
+ 'name' => 'puppet-agent',
+ );
+
+ return $services;
+}
diff --git a/sysutils/puppet-agent/src/opnsense/mvc/app/controllers/OPNsense/PuppetAgent/Api/ServiceController.php b/sysutils/puppet-agent/src/opnsense/mvc/app/controllers/OPNsense/PuppetAgent/Api/ServiceController.php
new file mode 100644
index 000000000..58c60f552
--- /dev/null
+++ b/sysutils/puppet-agent/src/opnsense/mvc/app/controllers/OPNsense/PuppetAgent/Api/ServiceController.php
@@ -0,0 +1,75 @@
+request->isPost()) {
+ // close session for long running action
+ $this->sessionClose();
+
+ $backend = new Backend();
+ // generate template
+ $backend->configdRun('template reload OPNsense/PuppetAgent');
+
+ $mdlPuppetAgent = new PuppetAgent();
+
+ // (res)start daemon
+ if ($mdlPuppetAgent->general->Enabled->__toString() == 1) {
+ $this->startAction();
+ }
+ // stop Puppet Agent when disabled
+ else {
+ $this->stopAction();
+ }
+ return array("status" => "ok");
+ }
+ else {
+ return array("status" => "failed");
+ }
+ }
+}
diff --git a/sysutils/puppet-agent/src/opnsense/mvc/app/controllers/OPNsense/PuppetAgent/Api/SettingsController.php b/sysutils/puppet-agent/src/opnsense/mvc/app/controllers/OPNsense/PuppetAgent/Api/SettingsController.php
new file mode 100644
index 000000000..fcd5e8861
--- /dev/null
+++ b/sysutils/puppet-agent/src/opnsense/mvc/app/controllers/OPNsense/PuppetAgent/Api/SettingsController.php
@@ -0,0 +1,93 @@
+request->isGet()) {
+ $mdlPuppetAgent = new PuppetAgent();
+ $result['puppetagent'] = $mdlPuppetAgent->getNodes();
+ }
+ return $result;
+ }
+
+ /**
+ * update PupppetAgent settings
+ * @return array status
+ * @throws \OPNsense\Base\ModelException
+ * @throws \ReflectionException
+ */
+ public function setAction()
+ {
+ $result = array("result" => "failed");
+ if ($this->request->isPost()) {
+ // load model and update with provided data
+ $mdlPuppetAgent = new PuppetAgent();
+ $mdlPuppetAgent->setNodes($this->request->getPost("puppetagent"));
+
+ // perform validation
+ $valMsgs = $mdlPuppetAgent->performValidation();
+ foreach ($valMsgs as $field => $msg) {
+ if (!array_key_exists("validations", $result)) {
+ $result["validations"] = array();
+ }
+ $result["validations"]["puppetagent." . $msg->getField()] = $msg->getMessage();
+ }
+
+ // serialize model to config and save
+ if ($valMsgs->count() == 0) {
+ $mdlPuppetAgent->serializeToConfig();
+ Config::getInstance()->save();
+ $result["result"] = "saved";
+ }
+ }
+ return $result;
+ }
+}
diff --git a/sysutils/puppet-agent/src/opnsense/mvc/app/controllers/OPNsense/PuppetAgent/IndexController.php b/sysutils/puppet-agent/src/opnsense/mvc/app/controllers/OPNsense/PuppetAgent/IndexController.php
new file mode 100644
index 000000000..d5398024d
--- /dev/null
+++ b/sysutils/puppet-agent/src/opnsense/mvc/app/controllers/OPNsense/PuppetAgent/IndexController.php
@@ -0,0 +1,47 @@
+view->pick('OPNsense/PuppetAgent/index');
+ // fetch form data "general" in
+ $this->view->generalForm = $this->getForm("general");
+ }
+}
diff --git a/sysutils/puppet-agent/src/opnsense/mvc/app/controllers/OPNsense/PuppetAgent/forms/general.xml b/sysutils/puppet-agent/src/opnsense/mvc/app/controllers/OPNsense/PuppetAgent/forms/general.xml
new file mode 100644
index 000000000..6713c5ec4
--- /dev/null
+++ b/sysutils/puppet-agent/src/opnsense/mvc/app/controllers/OPNsense/PuppetAgent/forms/general.xml
@@ -0,0 +1,20 @@
+
diff --git a/sysutils/puppet-agent/src/opnsense/mvc/app/models/OPNsense/PuppetAgent/ACL/ACL.xml b/sysutils/puppet-agent/src/opnsense/mvc/app/models/OPNsense/PuppetAgent/ACL/ACL.xml
new file mode 100644
index 000000000..b65ad7e65
--- /dev/null
+++ b/sysutils/puppet-agent/src/opnsense/mvc/app/models/OPNsense/PuppetAgent/ACL/ACL.xml
@@ -0,0 +1,9 @@
+
+
+ Services: Puppet Agent
+
+ ui/puppetagent/*
+ api/puppetagent/*
+
+
+
diff --git a/sysutils/puppet-agent/src/opnsense/mvc/app/models/OPNsense/PuppetAgent/Menu/Menu.xml b/sysutils/puppet-agent/src/opnsense/mvc/app/models/OPNsense/PuppetAgent/Menu/Menu.xml
new file mode 100644
index 000000000..e0c6f69d7
--- /dev/null
+++ b/sysutils/puppet-agent/src/opnsense/mvc/app/models/OPNsense/PuppetAgent/Menu/Menu.xml
@@ -0,0 +1,5 @@
+
diff --git a/sysutils/puppet-agent/src/opnsense/mvc/app/models/OPNsense/PuppetAgent/PuppetAgent.php b/sysutils/puppet-agent/src/opnsense/mvc/app/models/OPNsense/PuppetAgent/PuppetAgent.php
new file mode 100644
index 000000000..0dd518a3c
--- /dev/null
+++ b/sysutils/puppet-agent/src/opnsense/mvc/app/models/OPNsense/PuppetAgent/PuppetAgent.php
@@ -0,0 +1,38 @@
+
+ //OPNsense/puppetagent
+
+ Manage Puppet Agent service
+
+
+
+
+
+
+ 0
+ Y
+
+
+ Y
+
+
+ Y
+ /^.{1,100}$/u
+ Should be a string between 1 and 100 characters.
+
+
+
+
diff --git a/sysutils/puppet-agent/src/opnsense/mvc/app/views/OPNsense/PuppetAgent/index.volt b/sysutils/puppet-agent/src/opnsense/mvc/app/views/OPNsense/PuppetAgent/index.volt
new file mode 100644
index 000000000..289cf4e88
--- /dev/null
+++ b/sysutils/puppet-agent/src/opnsense/mvc/app/views/OPNsense/PuppetAgent/index.volt
@@ -0,0 +1,62 @@
+{#
+
+OPNsense® is Copyright © 2021 Jan Winkler
+OPNsense® is Copyright © 2014 – 2015 by Deciso B.V.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+#}
+
+
+
+
+
+
+
+
+ {{ partial("layout_partials/base_form",['fields':generalForm,'id':'frm_GeneralSettings'])}}
+
+
+
+
+
diff --git a/sysutils/puppet-agent/src/opnsense/service/conf/actions.d/actions_puppetagent.conf b/sysutils/puppet-agent/src/opnsense/service/conf/actions.d/actions_puppetagent.conf
new file mode 100644
index 000000000..808144953
--- /dev/null
+++ b/sysutils/puppet-agent/src/opnsense/service/conf/actions.d/actions_puppetagent.conf
@@ -0,0 +1,23 @@
+[start]
+command:/usr/local/etc/rc.d/puppet start
+parameters:
+type:script
+message:starting puppet agent
+
+[stop]
+command:/usr/local/etc/rc.d/puppet onestop
+parameters:
+type:script
+message:stop puppet agent
+
+[restart]
+command:/usr/local/etc/rc.d/puppet restart
+parameters:
+type:script
+message:restart puppet agent
+
+[status]
+command:/usr/local/etc/rc.d/puppet onestatus; exit 0
+parameters:
+type:script_output
+message:request puppet agent status
diff --git a/sysutils/puppet-agent/src/opnsense/service/templates/OPNsense/PuppetAgent/+TARGETS b/sysutils/puppet-agent/src/opnsense/service/templates/OPNsense/PuppetAgent/+TARGETS
new file mode 100644
index 000000000..085fa8c75
--- /dev/null
+++ b/sysutils/puppet-agent/src/opnsense/service/templates/OPNsense/PuppetAgent/+TARGETS
@@ -0,0 +1,2 @@
+puppetagent.conf:/usr/local/etc/puppet/puppet.conf
+rc.conf.d:/etc/rc.conf.d/puppet
diff --git a/sysutils/puppet-agent/src/opnsense/service/templates/OPNsense/PuppetAgent/puppetagent.conf b/sysutils/puppet-agent/src/opnsense/service/templates/OPNsense/PuppetAgent/puppetagent.conf
new file mode 100644
index 000000000..1319d02e8
--- /dev/null
+++ b/sysutils/puppet-agent/src/opnsense/service/templates/OPNsense/PuppetAgent/puppetagent.conf
@@ -0,0 +1,10 @@
+{% if helpers.exists('OPNsense.puppetagent.general') and OPNsense.puppetagent.general.Enabled|default("0") == "1" %}
+[main]
+certname = {{ system.hostname|lower }}.{{ system.domain|lower }}
+server = {{ OPNsense.puppetagent.general.FQDN|default("") }}
+{% if helpers.exists('OPNsense.puppetagent.general') and not helpers.empty('OPNsense.puppetagent.general.Environment') %}
+[agent]
+environment = {{ OPNsense.puppetagent.general.Environment|default("") }}
+{% endif %}
+{% endif %}
+
diff --git a/sysutils/puppet-agent/src/opnsense/service/templates/OPNsense/PuppetAgent/rc.conf.d b/sysutils/puppet-agent/src/opnsense/service/templates/OPNsense/PuppetAgent/rc.conf.d
new file mode 100644
index 000000000..a36c64f16
--- /dev/null
+++ b/sysutils/puppet-agent/src/opnsense/service/templates/OPNsense/PuppetAgent/rc.conf.d
@@ -0,0 +1,5 @@
+{% if helpers.exists('OPNsense.puppetagent.general') and OPNsense.puppetagent.general.Enabled|default("0") == "1" %}
+puppet_enable="YES"
+{% else %}
+puppet_enable="NO"
+{% endif %}