diff --git a/CHANGELOG.md b/CHANGELOG.md
index c1493a52b951a638d318f95710a867985e149028..6713f12d031013c8eafddd793be42fcef451fe6d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
 
 All notable changes to this project will be documented in this file.
 
+## Release 0.4.0
+
+* Added file/directory check.
+
 ## Release 0.3.1
 
 * Fixed a bug with long process names.
diff --git a/README.md b/README.md
index 0803b87ced28d84ec45c81bc632b124718d0848c..38c1b6d3a8ebec0d37c5bf8f9f76a22fac32bc60 100644
--- a/README.md
+++ b/README.md
@@ -78,18 +78,54 @@ server_patching::validate::exports:
 server_patching::validate::zfs_pools:
   - pool1
   - pool2
+server_patching::validate::files:
+  - path: /var/log/application.log
+    exists: true
+    type: regular file # file types are as outputted by stat(1)
+    owner: application
+    group: operations
+    mode: 644
+    age: 5m # maximum modification time format XXdXXhXXmXXs with any omited
+    min_size: 10
+    max_size: 6M
+    contains:
+      - application startup success
+      - connection established
+    not_contains:
+      - startup failed
+      - exception 24B70 in object.getConnected
+    last_lines: 100 # or 'first_lines: 100', but not both
+  - path: /srv/data/project
+    type: directory
+    owner: pete
+    group: staff
+    min_size: 5M # allocated space, calculation may take time
+    max_size: 10G # allocated space, calculation may take time
 ```
 
 The fragment generates verifies if:
 * VMWare tools service is running
 * CrowdStrike daemon process is running
-* Java process which has *solr* in its command line is running
 * NetDB URL redirects somewhere (to WebAuth presumably)
-* NetDB server 171.67.5.154 status URL returns success
+* NetDB status URL returns success
 * SSH port is open over IPv4
 * Telnet port is closed over IPv4
 * Given ZFS pools are imported
 * Given NFS shares are exported
+* Verfies that application log file
+    * exists
+    * is a regular file ("directory" type is supported as well)
+    * has correct permissions
+    * has been modified within last 5 min
+    * larger than 10 bytes, but smaller than 6MB
+    * contains both strings "application startup success" and "connection established" (Perl regular expressions supported)
+    * contains neither "startup failed" nor "exception 24B70 in object.getConnectd"
+    * only last 100 lines in the file are searched for these strings
+* Checks if a project directory:
+    * exists
+    * has correct premissions
+    * has an allocated size between 5MB and 10GB
+    * (text searches within directory files not supported)
 
 The module would generate a validation script `validation.sh` in
 `/usr/local/bin/` directory, which can be triggered by SSM and return a zero
diff --git a/REFERENCE.md b/REFERENCE.md
index 5d75d0bd44c2aa250d2bdb18bf3f46d73f016296..a12ca7165b39c008ca016ab9cab673e9fa05d98a 100644
--- a/REFERENCE.md
+++ b/REFERENCE.md
@@ -7,7 +7,7 @@
 ### Classes
 
 * [`server_patching`](#server_patching): A class to deploy patching-related tools
-* [`server_patching::validate`](#server_patchingvalidate): Validate the server state based on predefined checks.
+* [`server_patching::validate`](#server_patching--validate): Validate the server state based on predefined checks.
 
 ## Classes
 
@@ -23,7 +23,7 @@ At the moment only includes the class `server_patching::validate`.
 include server_patching
 ```
 
-### <a name="server_patchingvalidate"></a>`server_patching::validate`
+### <a name="server_patching--validate"></a>`server_patching::validate`
 
 The class generates or removes the validation script for the
 state of the server. The resulting script can be automatically
@@ -64,6 +64,29 @@ server_patching::validate::exports:
 server_patching::validate::zfs_pools:
   - pool1
   - pool2
+server_patching::validate::files:
+  - path: /var/log/application.log
+    exists: true
+    type: regular file # file types are as outputted by stat(1)
+    owner: application
+    group: operations
+    mode: 644
+    age: 5m # maximum modification time format XXdXXhXXmXXs with any omited
+    min_size: 10
+    max_size: 6M
+    contains:
+      - application startup success
+      - connection established
+    not_contais:
+      - application failed
+      - error 2740
+    last_lines: 100 # or 'first_lines: 100', but not both
+  - path: /srv/data/project
+    type: directory
+    owner: pete
+    group: staff
+    min_size: 5M # allocated space, calculation may take time
+    max_size: 10G # allocated space, calculation may take time
 ```
 
 #### Examples
@@ -78,18 +101,19 @@ include server_patching::validate
 
 The following parameters are available in the `server_patching::validate` class:
 
-* [`ensure`](#ensure)
-* [`validation_script`](#validation_script)
-* [`use_remctl`](#use_remctl)
-* [`services`](#services)
-* [`processes`](#processes)
-* [`urls`](#urls)
-* [`ports`](#ports)
-* [`mounts`](#mounts)
-* [`exports`](#exports)
-* [`zfs_pools`](#zfs_pools)
+* [`ensure`](#-server_patching--validate--ensure)
+* [`validation_script`](#-server_patching--validate--validation_script)
+* [`wait`](#-server_patching--validate--wait)
+* [`services`](#-server_patching--validate--services)
+* [`processes`](#-server_patching--validate--processes)
+* [`urls`](#-server_patching--validate--urls)
+* [`ports`](#-server_patching--validate--ports)
+* [`mounts`](#-server_patching--validate--mounts)
+* [`exports`](#-server_patching--validate--exports)
+* [`zfs_pools`](#-server_patching--validate--zfs_pools)
+* [`files`](#-server_patching--validate--files)
 
-##### <a name="ensure"></a>`ensure`
+##### <a name="-server_patching--validate--ensure"></a>`ensure`
 
 Data type: `Enum['absent','present']`
 
@@ -97,7 +121,7 @@ Deploy or remove the validation script.
 
 Default value: `'present'`
 
-##### <a name="validation_script"></a>`validation_script`
+##### <a name="-server_patching--validate--validation_script"></a>`validation_script`
 
 Data type: `Stdlib::Unixpath`
 
@@ -105,15 +129,23 @@ Location of the validation script on the system.
 
 Default value: `'/usr/local/bin/validate.sh'`
 
-##### <a name="use_remctl"></a>`use_remctl`
+##### <a name="-server_patching--validate--wait"></a>`wait`
 
-Whether to create a remctl for validation script or not.
+Data type: `Optional[Integer[0]]`
 
-##### <a name="services"></a>`services`
+Wait for specified number of seconds before starting checks.
 
-Data type: `Array[Struct[{
-    'name'       => String,
-    'active'     => Optional[Boolean]}]]`
+Default value: `undef`
+
+##### <a name="-server_patching--validate--services"></a>`services`
+
+Data type:
+
+```puppet
+Array[Struct[{
+    'name'         => String,
+    'active'       => Optional[Boolean]}]]
+```
 
 Array of hashes, where each hash is describing a service and
 its desired state.
@@ -127,12 +159,16 @@ Defaults to `true`.
 
 Default value: `[]`
 
-##### <a name="processes"></a>`processes`
+##### <a name="-server_patching--validate--processes"></a>`processes`
 
-Data type: `Array[Struct[{
-    'name'       => String,
-    'command'    => Optional[String],
-    'running'    => Optional[Boolean]}]]`
+Data type:
+
+```puppet
+Array[Struct[{
+    'name'         => String,
+    'command'      => Optional[String],
+    'running'      => Optional[Boolean]}]]
+```
 
 Array of hashes, where each hash describes the name of a process
 and whether it is supposed to be running.
@@ -146,12 +182,16 @@ or `false` if it is not supposed to. Defaults to `true`.
 
 Default value: `[]`
 
-##### <a name="urls"></a>`urls`
+##### <a name="-server_patching--validate--urls"></a>`urls`
+
+Data type:
 
-Data type: `Array[Struct[{
-    'url'        => Stdlib::HTTPUrl,
-    'resolve_to' => Optional[Stdlib::IP::Address],
-    'status'     => Optional[Integer[100,510]]}]]`
+```puppet
+Array[Struct[{
+    'url'          => Stdlib::HTTPUrl,
+    'resolve_to'   => Optional[Stdlib::IP::Address],
+    'status'       => Optional[Integer[100,510]]}]]
+```
 
 Array of hashes, where each hash describes the web URL
 and the HTTP status code it is supposed to return
@@ -164,13 +204,17 @@ Options:
 
 Default value: `[]`
 
-##### <a name="ports"></a>`ports`
+##### <a name="-server_patching--validate--ports"></a>`ports`
 
-Data type: `Array[Struct[{
-    'port'       => Stdlib::Port,
-    'proto'      => Optional[Enum['tcp','udp']],
-    'ip_ver'     => Optional[Enum['ipv4','ipv6']],
-    'listening'  => Optional[Boolean]}]]`
+Data type:
+
+```puppet
+Array[Struct[{
+    'port'         => Stdlib::Port,
+    'proto'        => Optional[Enum['tcp','udp']],
+    'ip_ver'       => Optional[Enum['ipv4','ipv6']],
+    'listening'    => Optional[Boolean]}]]
+```
 
 Array of hashes, where each hash contains a port number, protocol,
 IP protocol version (IPv4/IPv6) and whether anything is expected
@@ -186,7 +230,7 @@ the port is not supposed to respond. Defaults to `true`.
 
 Default value: `[]`
 
-##### <a name="mounts"></a>`mounts`
+##### <a name="-server_patching--validate--mounts"></a>`mounts`
 
 Data type: `Array[Stdlib::Unixpath]`
 
@@ -194,7 +238,7 @@ Array of mounts to be checked for presence.
 
 Default value: `[]`
 
-##### <a name="exports"></a>`exports`
+##### <a name="-server_patching--validate--exports"></a>`exports`
 
 Data type: `Array[Stdlib::Unixpath]`
 
@@ -202,7 +246,7 @@ Array of NFS exports to be checked for presence. Only local exports are checked.
 
 Default value: `[]`
 
-##### <a name="zfs_pools"></a>`zfs_pools`
+##### <a name="-server_patching--validate--zfs_pools"></a>`zfs_pools`
 
 Data type: `Array[String]`
 
@@ -210,3 +254,74 @@ Array of ZFS pools to be checked for presence.
 
 Default value: `[]`
 
+##### <a name="-server_patching--validate--files"></a>`files`
+
+Data type:
+
+```puppet
+Array[Struct[{
+    'path'         => Stdlib::Unixpath,
+    'type'         => Optional[String[1]],
+    'exists'       => Optional[Boolean],
+    'owner'        => Optional[String[1]],
+    'group'        => Optional[String[1]],
+    'mode'         => Optional[Variant[String[3,4], Integer[0,3777]]],
+    'age'          => Optional[Pattern[/^(\d{0,}d)?(\d{0,2}h)?(\d{0,2}m)?(\d{0,2}s)?$/]],
+    'min_size'     => Optional[Variant[String, Integer[1]]],
+    'max_size'     => Optional[Variant[String, Integer[1]]],
+    'contains'     => Optional[Array[String[1]]],
+    'not_contains' => Optional[Array[String[1]]],
+    'first_lines'  => Optional[Integer[1]],
+    'last_lines'   => Optional[Integer[1]]}]]
+```
+
+Array of hashes specifying requirements for a file or directory
+
+Options:
+
+* **:path** `Stdlib::Unixpath`: Full file system path to a file or directory. Mandatory.
+* **Optional** `String`: :type
+Type of a file or directory as shown by stat(1). The most typical ones are
+`regular file`, 'directory', 'block special file', 'character specila file',
+'fifo', 'socket', 'regular empty file', etc.
+* **Optional** `Boolean`: :exists
+Tells if the file should exist or not.
+* **Optional** `String`: :owner
+Owner of a file or directory. UIDs not accepted, only resolved user names.
+* **Optional** `String`: :group
+Group of a file or directory. GIDs not accepted, only resolved group names.
+* **Optional** `String`: :mode
+Mode of a file or directory either as an integer or a string.
+* **Optional** `Pattern`: :age
+File or directory modification date should be more recent than a given age.
+Age is specified in a format XXXXdXXhXXmXXs, where X are digits and `d` stands
+for days, `h` for hours, `m` for minutes and `s` for seconds. Any of these
+can be omited, like `5m` or `1h15m`.
+* **Optional** `Variant[String,Integer]`: :min_size
+Size of the file should exceed this value. Size is specified by default in bytes.
+'K', 'M', 'G', 'T' Multipliers can be appended to signify decimal Kilobytes,
+Megabytes, Gigabytes and Terabytes.
+* **Optional** `Variant[String,Integer]`: :max_size
+Size of the file should not exceed this value. Size is specified by default in bytes.
+'K', 'M', 'G', 'T' Multipliers can be appended to signify decimal Kilobytes,
+Megabytes, Gigabytes and Terabytes.
+* **Optional** `Array[String]`: :contains
+An array of strings, all of which should be present in a text file. Perl regular
+expressions are supported as well. Check fails if any of the strings is not found.
+* **Optional** `Array[String]`: :not_contains
+An array of strings, none of which should be present in a text file. Perl regular
+expressions are supported as well. Check fails if any of the strings is found.
+* **Optional** `Integer`: :first_lines
+Only this number of lines counting from the beginning of a text file are
+searched for strings listed in `contains` parameter. Even if a string is present
+in a file, but is located later than this line number, the check would fail.
+This paremeter cannot be specified together with `last_lines`.
+* **Optional** `Integer`: :last_lines
+Only this number of lines counting from the end of a text file are
+searched for strings listed in `contains` parameter. Even if a string is present
+in a file, but is located later than this line number, the check would fail.
+This paremeter cannot be specified together with `first_lines`. If you do that,
+this parameter would be ignored in favor of `first_lines`.
+
+Default value: `[]`
+
diff --git a/manifests/validate.pp b/manifests/validate.pp
index 41d3a639e129d94848c93816cb99bbad334efe7d..e450a35407e9d58cb9ae088fd6c321a5e2c51149 100644
--- a/manifests/validate.pp
+++ b/manifests/validate.pp
@@ -40,6 +40,29 @@
 # server_patching::validate::zfs_pools:
 #   - pool1
 #   - pool2
+# server_patching::validate::files:
+#   - path: /var/log/application.log
+#     exists: true
+#     type: regular file # file types are as outputted by stat(1)
+#     owner: application
+#     group: operations
+#     mode: 644
+#     age: 5m # maximum modification time format XXdXXhXXmXXs with any omited
+#     min_size: 10
+#     max_size: 6M
+#     contains:
+#       - application startup success
+#       - connection established
+#     not_contais:
+#       - application failed
+#       - error 2740
+#     last_lines: 100 # or 'first_lines: 100', but not both
+#   - path: /srv/data/project
+#     type: directory
+#     owner: pete
+#     group: staff
+#     min_size: 5M # allocated space, calculation may take time
+#     max_size: 10G # allocated space, calculation may take time
 # ```
 #
 # @param ensure
@@ -48,8 +71,8 @@
 # @param validation_script
 #   Location of the validation script on the system.
 #
-# @param use_remctl
-#   Whether to create a remctl for validation script or not.
+# @param wait
+#   Wait for specified number of seconds before starting checks.
 #
 # @param services
 #   Array of hashes, where each hash is describing a service and
@@ -117,34 +140,109 @@
 # @param zfs_pools
 #   Array of ZFS pools to be checked for presence.
 #
+# @param files
+#   Array of hashes specifying requirements for a file or directory
+#
+# @option files [Stdlib::Unixpath] :path
+#   Full file system path to a file or directory. Mandatory.
+#
+# @option files Optional[String] :type
+#   Type of a file or directory as shown by stat(1). The most typical ones are
+#   `regular file`, 'directory', 'block special file', 'character specila file',
+#   'fifo', 'socket', 'regular empty file', etc.
+#
+# @option files Optional[Boolean] :exists
+#   Tells if the file should exist or not.
+#
+# @option files Optional[String] :owner
+#   Owner of a file or directory. UIDs not accepted, only resolved user names.
+#
+# @option files Optional[String] :group
+#   Group of a file or directory. GIDs not accepted, only resolved group names.
+#
+# @option files Optional[String] :mode
+#   Mode of a file or directory either as an integer or a string.
+#
+# @option files Optional[Pattern] :age
+#   File or directory modification date should be more recent than a given age.
+#   Age is specified in a format XXXXdXXhXXmXXs, where X are digits and `d` stands
+#   for days, `h` for hours, `m` for minutes and `s` for seconds. Any of these
+#   can be omited, like `5m` or `1h15m`.
+#
+# @option files Optional[Variant[String,Integer]] :min_size
+#   Size of the file should exceed this value. Size is specified by default in bytes.
+#   'K', 'M', 'G', 'T' Multipliers can be appended to signify decimal Kilobytes,
+#   Megabytes, Gigabytes and Terabytes.
+#
+# @option files Optional[Variant[String,Integer]] :max_size
+#   Size of the file should not exceed this value. Size is specified by default in bytes.
+#   'K', 'M', 'G', 'T' Multipliers can be appended to signify decimal Kilobytes,
+#   Megabytes, Gigabytes and Terabytes.
+#
+# @option files Optional[Array[String]] :contains
+#   An array of strings, all of which should be present in a text file. Perl regular
+#   expressions are supported as well. Check fails if any of the strings is not found.
+#
+# @option files Optional[Array[String]] :not_contains
+#   An array of strings, none of which should be present in a text file. Perl regular
+#   expressions are supported as well. Check fails if any of the strings is found.
+#
+# @option files Optional[Integer] :first_lines
+#   Only this number of lines counting from the beginning of a text file are
+#   searched for strings listed in `contains` parameter. Even if a string is present
+#   in a file, but is located later than this line number, the check would fail.
+#   This paremeter cannot be specified together with `last_lines`.
+#
+# @option files Optional[Integer] :last_lines
+#   Only this number of lines counting from the end of a text file are
+#   searched for strings listed in `contains` parameter. Even if a string is present
+#   in a file, but is located later than this line number, the check would fail.
+#   This paremeter cannot be specified together with `first_lines`. If you do that,
+#   this parameter would be ignored in favor of `first_lines`.
+#
 # @example
 #   include server_patching::validate
 #
 class server_patching::validate (
   Enum['absent','present'] $ensure = 'present',
   Stdlib::Unixpath $validation_script = '/usr/local/bin/validate.sh',
+  Optional[Integer[0]] $wait = undef,
   Array[Struct[{
-    'name'       => String,
-    'active'     => Optional[Boolean]}]] $services = [],
+    'name'         => String,
+    'active'       => Optional[Boolean]}]] $services = [],
   Array[Struct[{
-    'name'       => String,
-    'command'    => Optional[String],
-    'running'    => Optional[Boolean]}]] $processes = [],
+    'name'         => String,
+    'command'      => Optional[String],
+    'running'      => Optional[Boolean]}]] $processes = [],
   Array[Struct[{
-    'url'        => Stdlib::HTTPUrl,
-    'resolve_to' => Optional[Stdlib::IP::Address],
-    'status'     => Optional[Integer[100,510]]}]] $urls = [],
+    'url'          => Stdlib::HTTPUrl,
+    'resolve_to'   => Optional[Stdlib::IP::Address],
+    'status'       => Optional[Integer[100,510]]}]] $urls = [],
   Array[Struct[{
-    'port'       => Stdlib::Port,
-    'proto'      => Optional[Enum['tcp','udp']],
-    'ip_ver'     => Optional[Enum['ipv4','ipv6']],
-    'listening'  => Optional[Boolean]}]] $ports = [],
+    'port'         => Stdlib::Port,
+    'proto'        => Optional[Enum['tcp','udp']],
+    'ip_ver'       => Optional[Enum['ipv4','ipv6']],
+    'listening'    => Optional[Boolean]}]] $ports = [],
   Array[Stdlib::Unixpath] $mounts = [],
   Array[Stdlib::Unixpath] $exports = [],
   Array[String] $zfs_pools = [],
+  Array[Struct[{
+    'path'         => Stdlib::Unixpath,
+    'type'         => Optional[String[1]],
+    'exists'       => Optional[Boolean],
+    'owner'        => Optional[String[1]],
+    'group'        => Optional[String[1]],
+    'mode'         => Optional[Variant[String[3,4], Integer[0,3777]]],
+    'age'          => Optional[Pattern[/^(\d{0,}d)?(\d{0,2}h)?(\d{0,2}m)?(\d{0,2}s)?$/]],
+    'min_size'     => Optional[Variant[String, Integer[1]]],
+    'max_size'     => Optional[Variant[String, Integer[1]]],
+    'contains'     => Optional[Array[String[1]]],
+    'not_contains' => Optional[Array[String[1]]],
+    'first_lines'  => Optional[Integer[1]],
+    'last_lines'   => Optional[Integer[1]]}]] $files = [],
 ) {
 
-  if [$services, $processes, $urls, $ports, $mounts, $exports, $zfs_pools].any |$param| { !empty($param) } {
+  if [$services, $processes, $urls, $ports, $mounts, $exports, $zfs_pools, $files].any |$param| { !empty($param) } {
     concat { $validation_script:
       ensure => $ensure,
       mode   => '0700',
@@ -153,7 +251,7 @@ class server_patching::validate (
     concat::fragment { 'bash-header':
       target  => $validation_script,
       order   => '01',
-      content => epp('server_patching/validate/header.epp'),
+      content => epp('server_patching/validate/header.epp', { 'wait' => $wait }),
     }
 
     concat::fragment { 'bash-footer':
@@ -237,6 +335,18 @@ class server_patching::validate (
         content => epp('server_patching/validate/zpool.epp', { 'zfs_pools' => $zfs_pools }),
       }
     }
+
+    if !empty($files) {
+      $_files = $files.map |$file| {
+        { 'exists' => true } + $file
+      }
+
+      concat::fragment { 'validate-files':
+        target  => $validation_script,
+        order   => '08',
+        content => epp('server_patching/validate/files.epp', { 'files' => $_files }),
+      }
+    }
   } else {
     # If no checks are defined, no reason to keep an empty
     # validation script around.
diff --git a/metadata.json b/metadata.json
index ed59792a7fb0a345e1240d6d5dc0ddc9bb654c3d..b0fcfd0b76a09541e57c6f7c44fab7436211093c 100644
--- a/metadata.json
+++ b/metadata.json
@@ -1,6 +1,6 @@
 {
   "name": "ruthenium-server_patching",
-  "version": "0.3.1",
+  "version": "0.3.0",
   "author": "Ruthenium",
   "summary": "Generate bash script to valideate server state.",
   "license": "MIT",
@@ -50,9 +50,9 @@
     {
       "operatingsystem": "Ubuntu",
       "operatingsystemrelease": [
-        "18.04",
         "20.04",
-        "22.04"
+        "22.04",
+        "24.04"
       ]
     }
   ],
diff --git a/templates/validate/files.epp b/templates/validate/files.epp
new file mode 100644
index 0000000000000000000000000000000000000000..daf4033feee315b09679c8b2a28f1ec8c752e4f1
--- /dev/null
+++ b/templates/validate/files.epp
@@ -0,0 +1,100 @@
+<%- | Array[Hash] $files | -%>
+<%- $files.each |$file| { -%>
+# file <%= $file['path'] %>
+echo -n "File <%= $file['path'] -%>..."
+failed=0
+stats="$(stat --format='%a!%G!%U!%Y!%B!%F' "<%= $file['path'] %>" 2>/dev/null)"
+if [ $? -eq 0 ]; then
+	<%- if !$file['exists'] { -%>
+    failed=1
+	<%- } -%>
+	IFS=! read -r mode group owner age size type <<< $stats
+else
+	<%- if $file['exists'] { -%>
+    failed=1
+	<%- } -%>
+    :
+fi
+  <%- if 'age' in $file { -%>
+if [[ <%= $file['age'] %> =~ ([0-9]+)d ]]; then
+	days=${BASH_REMATCH[1]}
+fi
+if [[ <%= $file['age'] %> =~ ([0-9]+)h ]]; then
+	hours=${BASH_REMATCH[1]}
+fi
+if [[ <%= $file['age'] %> =~ ([0-9]+)m ]]; then
+	minutes=${BASH_REMATCH[1]}
+fi
+if [[ <%= $file['age'] %> =~ ([0-9]+)s ]]; then
+	minutes=${BASH_REMATCH[1]}
+fi
+interval_seconds=$((days * 86400 + hours * 3600 + minutes * 60 + seconds))
+current_epoch=$(date +%s)
+oldest_allowed=$((current_epoch - interval_seconds))
+if [ $age -lt $oldest_allowed ]; then
+    failed=1
+fi
+  <%- } -%>
+  <%- if 'type' in $file { -%>
+if ! [ "$type" = "<%= $file['type'] %>" ]; then
+    failed=1
+fi
+  <%- } -%>
+  <%- if 'owner' in $file { -%>
+if ! [ $owner = <%= $file['owner'] %> ]; then
+    failed=1
+fi
+  <%- } -%>
+  <%- if 'group' in $file { -%>
+if ! [ $group = <%= $file['group'] %> ]; then
+    failed=1
+fi
+  <%- } -%>
+  <%- if 'mode' in $file { -%>
+if ! [ $mode = <%= $file['mode'] %> ]; then
+    failed=1
+fi
+  <%- } -%>
+if [ "$type" = 'directory' ]; then
+    size=$(du -s --bock-size=1 <%= $file['path'] %> | cut -f1)
+else
+  <%- if ('contains' in $file) or ('not_contains' in $file) { -%>
+    <%- if 'first_lines' in $file {
+       $op = "head -n ${file['first_lines']}"
+    } elsif 'last_lines' in $file {
+       $op = "tail -n ${file['last_lines']}"
+    } else {
+       $op = "cat"
+    } -%>
+    <%- $file['contains'].each |$pattern| { -%>
+    if ! <%= $op %> '<%= $file['path'] %>' | grep -P '<%= $pattern %>' >/dev/null 2>&1; then
+        failed=1
+    fi
+    <%- } -%>
+    <%- $file['not_contains'].each |$pattern| { -%>
+    if <%= $op %> '<%= $file['path'] %>' | grep -P '<%= $pattern %>' >/dev/null 2>&1; then
+        failed=1
+    fi
+    <%- } -%>
+  <%- } -%>
+  :
+fi
+  <%- if 'min_size' in $file { -%>
+min_size=$(numfmt --from=auto <%= $file['min_size'] %>)
+if [ $size -lt $min_size ]; then
+    failed=1
+fi
+  <%- } -%>
+  <%- if 'max_size' in $file {-%>
+max_size=$(numfmt --from=auto <%= $file['max_size'] %>)
+if [ $size -gt $max_size ]; then
+    failed=1
+fi
+  <%- } -%>
+if [ $failed -eq 0 ]; then
+    echo " OK."
+else
+    echo " FAIL."
+    ((result+=1))
+fi
+<%- } %>
diff --git a/templates/validate/header.epp b/templates/validate/header.epp
index 30a531690a20de9c20f5eef530834ecf2ed535e6..e5dade7e97d251e0e72f862ff16c13d48c325bd5 100644
--- a/templates/validate/header.epp
+++ b/templates/validate/header.epp
@@ -1,3 +1,4 @@
+<%- | Optional[Integer[0]] $wait | -%>
 #!/bin/bash
 
 # Set $PATH for this script
@@ -5,3 +6,9 @@ export PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin
 
 # Overall result of a check
 result=0
+
+# Wait for some time before initiating the checks
+<%- if $wait =~ NotUndef { -%>
+sleep <%= $wait %>
+<%- } -%>
+
diff --git a/templates/validate/process.epp b/templates/validate/process.epp
index 0d4afe882d308d33e3112144574abd21a12a4ba3..4a1374e9cd95854dcdef23f756e39b21dc240720 100644
--- a/templates/validate/process.epp
+++ b/templates/validate/process.epp
@@ -4,7 +4,7 @@
 # process <%= $process['name'] %>
 echo -n "Process <%= $process['name'] -%>..."
 <%- if 'command' in $process { -%>
-pgrep -af <%= $process['name'] %> | grep <%= $process['command'] %> > /dev/null
+pgrep -fa <%= $process['name'] %> | grep <%= $process['command'] %> > /dev/null
 <%- } else { -%>
 pgrep -f <%= $process['name'] %> > /dev/null
 <%- } -%>