diff --git a/contrib/openldap/core-fd-conf.schema b/contrib/openldap/core-fd-conf.schema
index 98f049a175ac6c77e1068c7ae1ff49314d7bc9ce..191abfa0063155f7642731fe229b8e2c2f7444e0 100644
--- a/contrib/openldap/core-fd-conf.schema
+++ b/contrib/openldap/core-fd-conf.schema
@@ -361,6 +361,31 @@ attributetype ( 1.3.6.1.4.1.38414.8.17.2 NAME 'fdSnapshotBase'
   SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
   SINGLE-VALUE)
 
+attributetype ( 1.3.6.1.4.1.38414.8.17.3 NAME 'fdEnableAutomaticSnapshots'
+  DESC 'FusionDirectory - Weither or not to enable snapshots'
+  EQUALITY booleanMatch
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
+  SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.38414.8.17.4 NAME 'fdSnapshotMinRetention'
+    DESC 'Minimum number of snapshots to be kept in store'
+    EQUALITY integerMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+    SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.38414.8.17.5 NAME 'fdSnapshotRetentionDays'
+    DESC 'Number of days a snapshot should be kept'
+    EQUALITY integerMatch
+    SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+    SINGLE-VALUE )
+
+attributetype (  1.3.6.1.4.1.38414.8.17.6 NAME 'fdSnapshotSourceData'
+  DESC 'Possible Origin / Source of data received '
+  EQUALITY octetStringMatch
+  SUBSTR caseIgnoreSubstringsMatch
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.40)
+  SINGLE-VALUE)
+
 # Miscellaneous
 
 attributetype ( 1.3.6.1.4.1.38414.8.18.2 NAME 'fdTabHook'
@@ -643,9 +668,9 @@ objectclass ( 1.3.6.1.4.1.38414.8.2.1 NAME 'fusionDirectoryConf'
     fdPluginsMenuBlacklist $ fdManagementConfig $ fdManagementUserConfig $
     fdAclTabOnObjects $ fdDepartmentCategories $ fdAclTargetFilterLimit $
     fdIncrementalModifierStates $
-    fdSslCaCertPath $ fdSslKeyPath $ fdSslCertPath $
+    fdSslCaCertPath $ fdSslKeyPath $ fdSslCertPath $ fdSnapshotRetentionDays $ fdSnapshotSourceData $
     fdCasActivated $ fdCasServerCaCertPath $ fdCasHost $ fdCasPort $ fdCasContext $ fdCasVerbose $
-    fdLoginMethod $ fdCasLibraryBool $ fdCasClientServiceName
+    fdLoginMethod $ fdCasLibraryBool $ fdCasClientServiceName $ fdEnableAutomaticSnapshots $ fdSnapshotMinRetention
   ) )
 
 objectclass ( 1.3.6.1.4.1.38414.8.2.2 NAME 'fusionDirectoryPluginsConf'
diff --git a/contrib/openldap/core-fd.schema b/contrib/openldap/core-fd.schema
index cb5367d10d5a5691ddc52f054540b6f2fc23ad4b..da9453bad3c0a1cc1ae722f28fd9c10f1d793793 100644
--- a/contrib/openldap/core-fd.schema
+++ b/contrib/openldap/core-fd.schema
@@ -64,6 +64,12 @@ attributetype ( 1.3.6.1.4.1.38414.62.1.4 NAME 'fdSnapshotObjectType'
   SUBSTR caseIgnoreSubstringsMatch
   SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
 
+attributetype ( 1.3.6.1.4.1.38414.62.1.51 NAME 'fdSnapshotDataSource'
+  DESC 'FusionDirectory - snapshot data origin / source'
+  EQUALITY caseIgnoreMatch
+  SUBSTR caseIgnoreSubstringsMatch
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
+
 ##### Subscriptions Attributes ######
 
 attributetype ( 1.3.6.1.4.1.38414.62.11.1 NAME 'fdSubscriptionStartDate'
@@ -394,7 +400,7 @@ objectclass ( 1.3.6.1.4.1.10098.1.2.1.19.18 NAME 'gosaAcl'
 objectclass ( 1.3.6.1.4.1.10098.1.2.1.19.19 NAME 'gosaSnapshotObject'
   DESC 'GOsa - Container object for undo and snapshot data'
   SUP top STRUCTURAL
-  MUST ( gosaSnapshotTimestamp $ gosaSnapshotDN $ gosaSnapshotData )
+  MUST ( gosaSnapshotTimestamp $ gosaSnapshotDN $ gosaSnapshotData $ fdSnapshotDataSource )
   MAY  ( fdSnapshotObjectType $ description ) )
 
 ### New FusionDirectory Objectclass ###
diff --git a/include/management/class_management.inc b/include/management/class_management.inc
index 51631eb49df402d0f3f12e577592905407becb97..27f806e8c115addc6ebab784c8b89f6e73d0e7e5 100644
--- a/include/management/class_management.inc
+++ b/include/management/class_management.inc
@@ -1318,17 +1318,19 @@ class management implements FusionDirectoryDialog
 
   /*!
    * \brief  Creates a new snapshot entry
+   * If source arg is not set, default to 'FD'.
    */
-  function createSnapshot (string $dn, string $description)
+  function createSnapshot (string $dn, string $description, string $snapshotSource = 'FD')
   {
     global $ui;
+
     if (empty($dn) || ($this->currentDn !== $dn)) {
       trigger_error('There was a problem with the snapshot workflow');
       return;
     }
     $entry = $this->listing->getEntry($dn);
     if ($entry->snapshotCreationAllowed()) {
-      $this->snapHandler->createSnapshot($dn, $description, $entry->type);
+      $this->snapHandler->createSnapshot($dn, $description, $entry->type, $snapshotSource);
       logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $dn, 'Snapshot created!');
     } else {
       $error = new FusionDirectoryPermissionError(htmlescape(sprintf(_('You are not allowed to restore a snapshot for %s.'), $dn)));
diff --git a/include/management/snapshot/class_SnapshotCreateDialog.inc b/include/management/snapshot/class_SnapshotCreateDialog.inc
index 89aebcf3c702e8c2b320db91919e69378cac64ed..b8f7d50b84bb22d369cc3bc2e8a65736f8a50ff4 100644
--- a/include/management/snapshot/class_SnapshotCreateDialog.inc
+++ b/include/management/snapshot/class_SnapshotCreateDialog.inc
@@ -56,17 +56,46 @@ class SnapshotCreateDialog extends ManagementDialog
           ),
         ]
       ],
+      'dataSource' => [
+        'name'  => _('dataSource - only available via web-service.'),
+        'attrs' => [
+            new SelectAttribute(
+              'Data source', _('Origin / Source of the data'),
+              'snapshotSource', FALSE,
+            ),
+        ]
+      ],
     ];
   }
 
   function __construct (string $dn, management $parent, string $aclCategory)
   {
     parent::__construct(NULL, NULL, $parent);
+    // The attribut will be passed to parent for later saving, dataSource might require same logic.
     $this->attributesAccess['description']->setInLdap(FALSE);
+    $this->attributesAccess['snapshotSource']->setInLdap(FALSE);
+    $this->attributesAccess['snapshotSource']->setVisible(FALSE);
+
+    $recordedDataSources = $this->getLdapRecordedDataSources();
+    if (!empty($recordedDataSources)) {
+      $this->attributesAccess['snapshotSource']->setChoices($recordedDataSources);
+    }
+
     $this->object_dn    = $dn;
     $this->aclCategory  = $aclCategory;
   }
 
+  /*
+   * Retrieve the data sources from configuration.
+   */
+  public function getLdapRecordedDataSources () : array
+  {
+    global $config;
+    $recordedDataSources = $config->current['SNAPSHOTSOURCEDATA'];
+
+    return $recordedDataSources;
+  }
+
   /*!
    * \brief Get LDAP base to use for ACL checks
    */
@@ -116,7 +145,11 @@ class SnapshotCreateDialog extends ManagementDialog
 
   function save (): array
   {
-    $this->parent->createSnapshot($this->object_dn, $this->description);
+    // snapshotSource is always set but can be empty and must be defaulted.
+    if (empty($this->snapshotSource)) {
+      $this->snapshotSource = 'FD';
+    }
+    $this->parent->createSnapshot($this->object_dn, $this->description, $this->snapshotSource);
     return [];
   }
 
diff --git a/include/management/snapshot/class_SnapshotHandler.inc b/include/management/snapshot/class_SnapshotHandler.inc
index 34ad2e2fbfba8f8f098d9d0d0ebf71f915e1ec8e..130552e4a4df95117e4ec78a5125336b6b965bd4 100644
--- a/include/management/snapshot/class_SnapshotHandler.inc
+++ b/include/management/snapshot/class_SnapshotHandler.inc
@@ -197,8 +197,10 @@ class SnapshotHandler
    * \param string $description Snapshot description
    *
    * \param string $objectType Type of snapshotted object
+   *
+   * \param string $snapshotSource source of the data.
    */
-  function createSnapshot ($dn, string $description, string $objectType)
+  function createSnapshot ($dn, string $description, string $objectType, string $snapshotSource = 'FD')
   {
     global $config;
     if (!$this->enabled()) {
@@ -251,6 +253,7 @@ class SnapshotHandler
     $target['gosaSnapshotDN']       = $dn;
     $target['description']          = $description;
     $target['fdSnapshotObjectType'] = $objectType;
+    $target['fdSnapshotDataSource'] = $snapshotSource;
 
     /* Insert the new snapshot
        But we have to check first, if the given gosaSnapshotTimestamp
@@ -281,6 +284,48 @@ class SnapshotHandler
     logging::log('snapshot', 'create', $new_dn, array_keys($target), $ldap->get_error());
   }
 
+  // function verifing the configuration retention for snapshots.
+  // Remove snapshots from the user if retention rules approves.
+  public function verifySnapshotRetention (string $dn) : void
+  {
+    global $config;
+
+    $snapMinRetention  = $config->current['SNAPSHOTMINRETENTION'];
+    $snapRetentionDays = $config->current['SNAPSHOTRETENTIONDAYS'];
+
+    // calculate the epoch date on which snaps can be delete.
+    $todayMinusRetention = time() - ($snapRetentionDays * 24 * 60 * 60);
+    $snapDateToDelete = strtotime(date('Y-m-d H:i:s', $todayMinusRetention));
+
+    $dnSnapshotsList = $this->getSnapshots($dn, TRUE);
+    $snapToDelete = [];
+    $snapCount = 0;
+
+    if (isset($dnSnapshotsList) && !empty($dnSnapshotsList)) {
+      foreach ($dnSnapshotsList as $snap) {
+        $snapCount += 1;
+        // let's keep seconds instead of nanosecs
+        $snapEpoch = preg_split('/-/', $snap['gosaSnapshotTimestamp'][0]);
+        if ($snapEpoch[0] < $snapDateToDelete) {
+          $snapToDelete[] = $snap['dn'];
+        }
+      }
+    }
+
+    // The not empty is not mandatory but is more ressource friendly
+    if (!empty($snapToDelete) && ($snapCount > $snapMinRetention)) {
+      $snapToKeep = $snapCount - $snapMinRetention;
+      // Sort snapToDelete by old first DN timestamp is the only thing different.
+      sort($snapToDelete);
+      for ($i = 0; $i < $snapToKeep; $i++) {
+        // not empty required because array keeps on being iterated even if NULL object.
+        if (!empty($snapToDelete[$i])) {
+          $this->removeSnapshot($snapToDelete[$i]);
+        }
+      }
+    }
+  }
+
   /*!
    * \brief Remove a snapshot
    *
diff --git a/plugins/config/class_configInLdap.inc b/plugins/config/class_configInLdap.inc
index 6ff6597911033d52c75ef298d5a2f6af73afda1e..8cf0034b45ef282dcfce54cfaf8bb38848e47d40 100644
--- a/plugins/config/class_configInLdap.inc
+++ b/plugins/config/class_configInLdap.inc
@@ -101,16 +101,6 @@ class configInLdap extends simplePlugin
             'fdSchemaCheck', FALSE,
             TRUE
           ),
-          new BooleanAttribute(
-            _('Enable snapshots'), _('This enables you to save certain states of entries and restore them later on.'),
-            'fdEnableSnapshots', FALSE,
-            TRUE
-          ),
-          new StringAttribute(
-            _('Snapshot base'), _('The base where snapshots should be stored inside the LDAP directory.'),
-            'fdSnapshotBase', FALSE,
-            'ou=snapshots,'.$config->current['BASE']
-          ),
           new BooleanAttribute(
             _('Wildcard foreign keys'), _('Enables wildcard searches like member=* when moving a whole department. This will open all existing groups and roles to make sure foreign keys are respected. Slow on big trees.'),
             'fdWildcardForeignKeys', FALSE,
@@ -475,16 +465,6 @@ class configInLdap extends simplePlugin
 
     $this->fusionConfigMd5 = md5_file(CACHE_DIR."/".CLASS_CACHE);
 
-    $this->attributesAccess['fdEnableSnapshots']->setManagedAttributes(
-      [
-        'disable' => [
-          FALSE => [
-            'fdSnapshotBase',
-          ]
-        ]
-      ]
-    );
-
     $this->attributesAccess['fdForceSSL']->setManagedAttributes(
       [
         'disable' => [
diff --git a/plugins/config/class_snapshotConfig.inc b/plugins/config/class_snapshotConfig.inc
new file mode 100644
index 0000000000000000000000000000000000000000..8c260e08228c0982cba47d0f110a921d1a31bf7d
--- /dev/null
+++ b/plugins/config/class_snapshotConfig.inc
@@ -0,0 +1,106 @@
+<?php
+/*
+This code is part of FusionDirectory (http://www.fusiondirectory.org/)
+Copyright (C) 2012-2023 FusionDirectory
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+class snapshotsConfig extends simplePlugin
+{
+  static function plInfo (): array
+  {
+    return [
+      'plShortName'     => _('Snapshots'),
+      'plDescription'   => _('FusionDirectory Snapshot Configuration'),
+      'plObjectClass'   => ['fusionDirectoryConf'],
+      'plObjectType'    => ['configuration'],
+      'plProvidedAcls'  => parent::generatePlProvidedAcls(static::getAttributesInfo())
+    ];
+  }
+
+  static function getAttributesInfo (): array
+  {
+    global $config;
+
+    return [
+      'snapshotsConf' => [
+        'name'  => _('Snapshots Configuration'),
+        'attrs' => [
+          new BooleanAttribute(
+            _('Enable snapshots'), _('This enables you to save certain states of entries and restore them later on.'),
+            'fdEnableSnapshots', FALSE,
+            TRUE
+          ),
+          new BooleanAttribute(
+            _('Enable automatic snapshots'), _('This enables you to automatically create a snapshot upon saving if any modifications have been found.'),
+            'fdEnableAutomaticSnapshots', FALSE,
+            FALSE
+          ),
+          new StringAttribute(
+            _('Snapshot base'), _('The base where snapshots should be stored inside the LDAP directory.'),
+            'fdSnapshotBase', FALSE,
+            'ou=snapshots,'.$config->current['BASE']
+          ),
+        ]
+      ],
+      'snapshotsAdvanceConf' => [
+        'name'  => _('Snapshots Advance Configuration'),
+        'attrs' => [
+          new IntAttribute(
+            _('Minimum number of snapshots to be kept'), _('Set the minimum number of snapshots to be kept'),
+            'fdSnapshotMinRetention', FALSE, '', FALSE, ''
+          ),
+          new IntAttribute(
+            _('Retention time in days'), _('Set the retention time in days for a snapshots to be kept'),
+            'fdSnapshotRetentionDays', FALSE, '', FALSE, ''
+          ),
+        ]
+      ],
+      'OriginDataSource' => [
+        'name' => _('List of available sources / origin of data'),
+        'attrs' => [
+          new SetAttribute(
+            new StringAttribute(
+              _('Origin / source of data'), _('Origin / Source of data'),
+              'fdSnapshotSourceData', FALSE,
+            )
+          ),
+        ]
+      ],
+    ];
+  }
+
+  function __construct ($dn = NULL, $object = NULL, $parent = NULL, $mainTab = FALSE)
+  {
+    global $config;
+    parent::__construct($dn, $object, $parent, $mainTab);
+
+    $this->attributesAccess['fdEnableSnapshots']->setManagedAttributes(
+      [
+        'disable' => [
+          FALSE => [
+            'fdSnapshotBase',
+            'fdEnableAutomaticSnapshots',
+            'fdSnapshotMinRetention',
+            'fdSnapshotRetentionDays',
+          ]
+        ]
+      ]
+    );
+  }
+
+}
+
diff --git a/plugins/personal/generic/class_user.inc b/plugins/personal/generic/class_user.inc
index 35531802a2c968264821b0dfbc2935fc5f7a09d0..08f4f2053bf3fe8230c6e64064b54c325b369f20 100644
--- a/plugins/personal/generic/class_user.inc
+++ b/plugins/personal/generic/class_user.inc
@@ -375,7 +375,7 @@ class user extends simplePlugin
 
   function post_save ()
   {
-    global $ui;
+    global $ui, $config;
 
     /* Update current locale settings, if we have edited ourselves */
     if (isset($this->attrs['preferredLanguage']) && ($this->dn == $ui->dn)) {
@@ -383,10 +383,26 @@ class user extends simplePlugin
       session::set('ui', $ui);
       session::set('Last_init_lang', 'update');
     }
-
+    // Verification is snapshot is enabled and automatic.
+    // Note : string values are stored in that array.
+    if (isset($config->current['ENABLEAUTOMATICSNAPSHOTS']) && isset($config->current['ENABLESNAPSHOTS'])) {
+      if (strtolower($config->current['ENABLEAUTOMATICSNAPSHOTS']) === 'true' && strtolower($config->current['ENABLESNAPSHOTS']) === 'true' ) {
+        $this->generateAutomaticSnapshot();
+      }
+    }
     return parent::post_save();
   }
 
+  /*
+   * Create the snapshot object in case of automated snapshot.
+   */
+  public function generateAutomaticSnapshot ()
+  {
+    $snapshotHandler = new SnapshotHandler();
+    $snapshotHandler->createSnapshot($this->dn, 'automatic snapshot', 'USER', 'FD');
+    $snapshotHandler->verifySnapshotRetention($this->dn);
+  }
+
   function adapt_from_template (array $attrs, array $skip = [])
   {
     if ($this->uid != '') {