diff --git a/.gitignore b/.gitignore
index c817477a0aea992fe70e44977a62078cc9310079..d108e8468e273169842708a71daed76f79705ae0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,7 @@
 /spec/fixtures/modules/*
 /tmp/
 /vendor/
+/.vendor/
 /convert_report.txt
 /update_report.txt
 .DS_Store
@@ -26,6 +27,12 @@
 .envrc
 /inventory.yaml
 /spec/fixtures/litmus_inventory.yaml
+.resource_types
+.modules
+.task_cache.json
+.plan_cache.json
+.rerun.json
+bolt-debug.log
 *~
 \#*\#
 .\#*
diff --git a/.pdkignore b/.pdkignore
index 2cf6497d6b045521fe58051bd4756516b9fab93f..eff5820cb1491ab73281a6a140118813c169f8c7 100644
--- a/.pdkignore
+++ b/.pdkignore
@@ -19,6 +19,7 @@
 /spec/fixtures/modules/*
 /tmp/
 /vendor/
+/.vendor/
 /convert_report.txt
 /update_report.txt
 .DS_Store
@@ -26,12 +27,19 @@
 .envrc
 /inventory.yaml
 /spec/fixtures/litmus_inventory.yaml
+.resource_types
+.modules
+.task_cache.json
+.plan_cache.json
+.rerun.json
+bolt-debug.log
 *~
 \#*\#
 .\#*
 /.fixtures.yml
 /Gemfile
 /.gitattributes
+/.github/
 /.gitignore
 /.pdkignore
 /.puppet-lint.rc
diff --git a/.puppet-lint.rc b/.puppet-lint.rc
index cc96ece0513d69709b87af611173e2a6e4532f62..e9f2401c211aad60e80fcce3f7de9e62c7ddfba9 100644
--- a/.puppet-lint.rc
+++ b/.puppet-lint.rc
@@ -1 +1,3 @@
 --relative
+--no-80chars-check
+--ignore-paths=.vendor/**/*.pp,.bundle/**/*.pp,pkg/**/*.pp,spec/**/*.pp,tests/**/*.pp,types/**/*.pp,vendor/**/*.pp
diff --git a/.rubocop.yml b/.rubocop.yml
index 5be1f9fa994085890c3fe9db117a937b8928f8cb..21b82b99b8ed1e2fb1ae4fc9a08d9aeb91054a33 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -3,6 +3,7 @@ require:
 - rubocop-performance
 - rubocop-rspec
 AllCops:
+  NewCops: enable
   DisplayCopNames: true
   TargetRubyVersion: '2.6'
   Include:
@@ -527,6 +528,8 @@ Lint/DuplicateBranch:
   Enabled: false
 Lint/DuplicateMagicComment:
   Enabled: false
+Lint/DuplicateMatchPattern:
+  Enabled: false
 Lint/DuplicateRegexpCharacterClassElement:
   Enabled: false
 Lint/EmptyBlock:
@@ -643,6 +646,8 @@ Style/ComparableClamp:
   Enabled: false
 Style/ConcatArrayLiterals:
   Enabled: false
+Style/DataInheritance:
+  Enabled: false
 Style/DirEmpty:
   Enabled: false
 Style/DocumentDynamicEvalDefinition:
@@ -711,6 +716,8 @@ Style/RedundantHeredocDelimiterQuotes:
   Enabled: false
 Style/RedundantInitialize:
   Enabled: false
+Style/RedundantLineContinuation:
+  Enabled: false
 Style/RedundantSelfAssignmentBranch:
   Enabled: false
 Style/RedundantStringEscape:
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index a5f6a2b80eb07663d44f5e705340b4e116bb0b67..902e5f8b8d95d3c0c2c4db3579ce41dd628eb230 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -5,5 +5,6 @@
     "ms-vscode.powershell",
     "EditorConfig.EditorConfig",
     "glenbuktenica.unicode-substitutions"
+    "Shopify.ruby-lsp"
   ]
 }
diff --git a/Gemfile b/Gemfile
index add18735e8bfdc5cd958d1dbba2833dcce9e4853..dc6cbe053f8f75161f8558a5ac5dd5e91f1ca40d 100644
--- a/Gemfile
+++ b/Gemfile
@@ -20,25 +20,33 @@ group :development do
   gem "json", '= 2.6.1',                         require: false if Gem::Requirement.create(['>= 3.1.0', '< 3.1.3']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup))
   gem "json", '= 2.6.3',                         require: false if Gem::Requirement.create(['>= 3.2.0', '< 4.0.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup))
   gem "racc", '~> 1.4.0',                        require: false if Gem::Requirement.create(['>= 2.7.0', '< 3.0.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup))
+  gem "deep_merge", '~> 1.2.2',                  require: false
   gem "voxpupuli-puppet-lint-plugins", '~> 5.0', require: false
-  gem "facterdb", '~> 1.18',                     require: false
-  gem "metadata-json-lint", '~> 3.0',            require: false
-  gem "puppetlabs_spec_helper", '~> 6.0',        require: false
-  gem "rspec-puppet-facts", '~> 2.0',            require: false
-  gem "codecov", '~> 0.2',                       require: false
+  gem "facterdb", '~> 2.1',                      require: false if Gem::Requirement.create(['< 3.0.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup))
+  gem "facterdb", '~> 3.0',                      require: false if Gem::Requirement.create(['>= 3.0.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup))
+  gem "metadata-json-lint", '~> 4.0',            require: false
+  gem "json-schema", '< 5.1.1',                  require: false
+  gem "rspec-puppet-facts", '~> 4.0',            require: false if Gem::Requirement.create(['< 3.0.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup))
+  gem "rspec-puppet-facts", '~> 5.0',            require: false if Gem::Requirement.create(['>= 3.0.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup))
   gem "dependency_checker", '~> 1.0.0',          require: false
   gem "parallel_tests", '= 3.12.1',              require: false
   gem "pry", '~> 0.10',                          require: false
-  gem "simplecov-console", '~> 0.5',             require: false
+  gem "simplecov-console", '~> 0.9',             require: false
   gem "puppet-debugger", '~> 1.0',               require: false
-  gem "rubocop", '= 1.48.1',                     require: false
+  gem "rubocop", '~> 1.50.0',                    require: false
   gem "rubocop-performance", '= 1.16.0',         require: false
   gem "rubocop-rspec", '= 2.19.0',               require: false
   gem "rb-readline", '= 0.5.5',                  require: false, platforms: [:mswin, :mingw, :x64_mingw]
 end
+group :development, :release_prep do
+  gem "puppet-strings", '~> 4.0',         require: false
+  gem "puppetlabs_spec_helper", '~> 8.0', require: false
+  gem "puppet-blacksmith", '~> 7.0',      require: false
+end
 group :system_tests do
-  gem "puppet_litmus", '~> 1.0', require: false, platforms: [:ruby, :x64_mingw]
-  gem "serverspec", '~> 2.41',   require: false
+  gem "puppet_litmus", '~> 1.0',   require: false, platforms: [:ruby, :x64_mingw]
+  gem "CFPropertyList", '< 3.0.7', require: false, platforms: [:mswin, :mingw, :x64_mingw]
+  gem "serverspec", '~> 2.41',     require: false
 end
 
 puppet_version = ENV['PUPPET_GEM_VERSION']
diff --git a/Rakefile b/Rakefile
index 74415a96f53d00b92550f6c697bb02093faecf76..c7d21ca057b7df78065b4855e85d175967a439af 100644
--- a/Rakefile
+++ b/Rakefile
@@ -4,85 +4,10 @@ require 'bundler'
 require 'puppet_litmus/rake_tasks' if Gem.loaded_specs.key? 'puppet_litmus'
 require 'puppetlabs_spec_helper/rake_tasks'
 require 'puppet-syntax/tasks/puppet-syntax'
-require 'github_changelog_generator/task' if Gem.loaded_specs.key? 'github_changelog_generator'
 require 'puppet-strings/tasks' if Gem.loaded_specs.key? 'puppet-strings'
 
-def changelog_user
-  return unless Rake.application.top_level_tasks.include? "changelog"
-  returnVal = nil || JSON.load(File.read('metadata.json'))['author']
-  raise "unable to find the changelog_user in .sync.yml, or the author in metadata.json" if returnVal.nil?
-  puts "GitHubChangelogGenerator user:#{returnVal}"
-  returnVal
-end
-
-def changelog_project
-  return unless Rake.application.top_level_tasks.include? "changelog"
-
-  returnVal = nil
-  returnVal ||= begin
-    metadata_source = JSON.load(File.read('metadata.json'))['source']
-    metadata_source_match = metadata_source && metadata_source.match(%r{.*\/([^\/]*?)(?:\.git)?\Z})
-
-    metadata_source_match && metadata_source_match[1]
-  end
-
-  raise "unable to find the changelog_project in .sync.yml or calculate it from the source in metadata.json" if returnVal.nil?
-
-  puts "GitHubChangelogGenerator project:#{returnVal}"
-  returnVal
-end
-
-def changelog_future_release
-  return unless Rake.application.top_level_tasks.include? "changelog"
-  returnVal = "v%s" % JSON.load(File.read('metadata.json'))['version']
-  raise "unable to find the future_release (version) in metadata.json" if returnVal.nil?
-  puts "GitHubChangelogGenerator future_release:#{returnVal}"
-  returnVal
-end
-
 PuppetLint.configuration.send('disable_relative')
-
-
-if Gem.loaded_specs.key? 'github_changelog_generator'
-  GitHubChangelogGenerator::RakeTask.new :changelog do |config|
-    raise "Set CHANGELOG_GITHUB_TOKEN environment variable eg 'export CHANGELOG_GITHUB_TOKEN=valid_token_here'" if Rake.application.top_level_tasks.include? "changelog" and ENV['CHANGELOG_GITHUB_TOKEN'].nil?
-    config.user = "#{changelog_user}"
-    config.project = "#{changelog_project}"
-    config.future_release = "#{changelog_future_release}"
-    config.exclude_labels = ['maintenance']
-    config.header = "# Change log\n\nAll notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org)."
-    config.add_pr_wo_labels = true
-    config.issues = false
-    config.merge_prefix = "### UNCATEGORIZED PRS; LABEL THEM ON GITHUB"
-    config.configure_sections = {
-      "Changed" => {
-        "prefix" => "### Changed",
-        "labels" => ["backwards-incompatible"],
-      },
-      "Added" => {
-        "prefix" => "### Added",
-        "labels" => ["enhancement", "feature"],
-      },
-      "Fixed" => {
-        "prefix" => "### Fixed",
-        "labels" => ["bug", "documentation", "bugfix"],
-      },
-    }
-  end
-else
-  desc 'Generate a Changelog from GitHub'
-  task :changelog do
-    raise <<EOM
-The changelog tasks depends on recent features of the github_changelog_generator gem.
-Please manually add it to your .sync.yml for now, and run `pdk update`:
----
-Gemfile:
-  optional:
-    ':development':
-      - gem: 'github_changelog_generator'
-        version: '~> 1.15'
-        condition: "Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.3.0')"
-EOM
-  end
-end
+PuppetLint.configuration.send('disable_80chars')
+PuppetLint.configuration.fail_on_warnings = false
+PuppetLint.configuration.ignore_paths = [".vendor/**/*.pp", ".bundle/**/*.pp", "pkg/**/*.pp", "spec/**/*.pp", "tests/**/*.pp", "types/**/*.pp", "vendor/**/*.pp"]
 
diff --git a/files/squid/helpers/rules.d/office.forms.rules b/files/squid/helpers/rules.d/office.forms.rules
new file mode 100644
index 0000000000000000000000000000000000000000..93e42c2d1c22d76b2b333a493d8470b98adf3f38
--- /dev/null
+++ b/files/squid/helpers/rules.d/office.forms.rules
@@ -0,0 +1,17 @@
+// For TDDC77 DAT3 2025-01
+^https://forms\.office\.com/pages/responsepage\.aspx\?id=7Bg_kSZ_X0yoFnhP6aWO3dxPk6SaoB1DvjOxHiii8UZUMExJR0pRWkMzOTYzWjRGNzJHTFJMNUhFSS4u&route=shorturl true
+^https://forms\.office\.com/e/N8TBYTcMSD true
+
+// https://forms\.office\.com/formapi/api/913f18ec-7f26-4c5f-a816-784fe9a58edd/users/a4934fdc-a09a-431d-be33-b11e28a2f146/light/runtimeFormsWithResponses('7Bg_kSZ_X0yoFnhP6aWO3dxPk6SaoB1DvjOxHiii8UZUMExJR0pRWkMzOTYzWjRGNzJHTFJMNUhFSS4u')\?\$expand=questions(\$expand=choices)&\$top=1 true
+^https://forms\.office\.com/formapi/api/.* true
+
+// https://lists.office.com/Images/913f18ec-7f26-4c5f-a816-784fe9a58edd/a4934fdc-a09a-431d-be33-b11e28a2f146/T0LIGJQZC3963Z4F72GLRL5HEI/69d43e42-bad1-4343-a566-e274f56a5519
+^https://lists.office.com/Images/.* true
+
+^https://eu-mobile\.events\.data\.microsoft\.com/OneCollector/1\.0/\?.* true
+^https://forms\.office\.com/pwa/en-gb/app\.webmanifest true
+^https://cdn\.forms\.office\.net/scripts/dists/[-_.a-zA-Z0-9]*\.js true
+^https://c\.office\.com/c\.gif true
+^https://cdn\.forms\.office.net/images/.* true
+^https://forms\.cloud\.microsoft/muid\.gif\?muid=.* true
+^https://cdn\.forms\.office\.net/formsresources/eventcard/cover/reunion_people\.jpg true
diff --git a/files/squid/helpers/rules.d/python.rules b/files/squid/helpers/rules.d/python.rules
index 39c000d31415d28fa1351e36f3633c4924d7a47c..dcca136d0e23121f9c9f269f07fa945ba4befb2c 100644
--- a/files/squid/helpers/rules.d/python.rules
+++ b/files/squid/helpers/rules.d/python.rules
@@ -34,3 +34,25 @@
 ^https?://scikit-learn\.org/stable/modules/.* true
 ^https?://scikit-learn\.org/stable/_static/.* true
 ^https?://scikit-learn\.org/dev/_static/.* true
+
+// TDDE69
+^https://views\.scientific-python\.org/js/script\.js true
+^https://cdn\.jsdelivr\.net/npm/.*/fonts/.*\.woff2? true
+^https://cdn\.jsdelivr\.net/npm/.*\.css true
+^https://cdn\.datatables\.net/[.0-9]*/js/.*\.js true
+
+^https://scikit-learn\.org/stable/user_guide\.html true
+^https://scikit-learn\.org/stable/_images/.* true
+
+^https://matplotlib\.org/stable/users/.* true
+^https://matplotlib\.org/stable/api/.* true
+^https://matplotlib\.org/stable/_static/.* true
+^https://matplotlib\.org/stable/_images/.* true
+^https://matplotlib\.org/cdn-cgi/scripts/.*\.js true
+
+// TDDE69 Less important
+^https://pandas\.pydata\.org/docs/.* true
+^https://pandas\.pydata\.org/static/.* true
+^https://pandas\.pydata\.org/versions\.json true
+
+^https://pytorch\.org/docs/stable/.* true
diff --git a/files/squid/helpers/squid-url-rewrite.py b/files/squid/helpers/squid-url-rewrite.py
index 241dea7eb3230a5e35cb70609ae765478bf49f26..7b4f33dbe6f5bb364b3dc9d979f7fe13259ffaa1 100755
--- a/files/squid/helpers/squid-url-rewrite.py
+++ b/files/squid/helpers/squid-url-rewrite.py
@@ -84,25 +84,35 @@ def main():
         ruleset.append( [re.compile(errorrex), True] )
         ruleset.append( [re.compile("^.*"), False] )
 
-    devel_rules = basedir + "/../devel.rules"
-    if os.path.isfile(devel_rules):
-        load_rules(ruleset, devel_rules)
-
-    # load opendsa first to let it override default rules
-    load_rules(ruleset, basedir + "/rules.d/opendsa.rules")
-    load_rules(ruleset, basedir + "/rules.d/default.rules")
-    load_rules(ruleset, basedir + "/rules.d/rstudio.rules")
-    load_rules(ruleset, basedir + "/rules.d/cplusplus.rules")
-    load_rules(ruleset, basedir + "/rules.d/python.rules")
-    load_rules(ruleset, basedir + "/rules.d/java.rules")
-    load_rules(ruleset, basedir + "/rules.d/ruby.rules")
-    load_rules(ruleset, basedir + "/rules.d/sas.rules")
-    load_rules(ruleset, basedir + "/rules.d/translate.rules")
+    # Load all rules to a temporary ruleset and then add it to the rules
+    # Failure to load rules will then lead to complete denial of service and be noticed
+    tmpruleset = list()
+    try:
+        devel_rules = basedir + "/../devel.rules"
+        if os.path.isfile(devel_rules):
+            load_rules(tmpruleset, devel_rules)
+
+        # load opendsa first to let it override default rules
+        load_rules(tmpruleset, basedir + "/rules.d/opendsa.rules")
+        load_rules(tmpruleset, basedir + "/rules.d/default.rules")
+        load_rules(tmpruleset, basedir + "/rules.d/rstudio.rules")
+        load_rules(tmpruleset, basedir + "/rules.d/cplusplus.rules")
+        load_rules(tmpruleset, basedir + "/rules.d/python.rules")
+        load_rules(tmpruleset, basedir + "/rules.d/java.rules")
+        load_rules(tmpruleset, basedir + "/rules.d/ruby.rules")
+        load_rules(tmpruleset, basedir + "/rules.d/sas.rules")
+        load_rules(tmpruleset, basedir + "/rules.d/translate.rules")
+        load_rules(tmpruleset, basedir + "/rules.d/office.forms.rules")
+
+        ruleset.extend(tmpruleset)
+        
+    except Exception as e:
+        sys.stderr.write( str( e ) )
+        sys.stderr.flush()
 
     with open(filterlog, 'a') as log:
-        try:
-            while True:
-            
+        while True:
+            try:
                 line = sys.stdin.readline().strip()
 
                 log.write('{}: {}\n'.format(datetime.datetime.now().strftime("%Y-%m-%d_%H-%M"), line))
@@ -118,8 +128,8 @@ def main():
 
                 sys.stdout.write(new_url + '\n')
                 sys.stdout.flush()
-        except Exception as e:
-            sys.stderr.write( e )
-            sys.stderr.flush()
+            except Exception as e:
+                sys.stderr.write( str( e ) )
+                sys.stderr.flush()
 
 main()
diff --git a/manifests/init.pp b/manifests/init.pp
index 4a64d65e788a633f69bd14253c8dc68b56f602a8..f09f644a2fbf3b575bede5e694b7157f48799b9f 100644
--- a/manifests/init.pp
+++ b/manifests/init.pp
@@ -117,12 +117,6 @@ class aes {
   }
 
   if $facts[networking][fqdn] == 'aes-devel.edu.liu.se' {
-    ::users::liu_user { 'malni83':
-      commonname => 'Malte Nilsson',
-      shell      => '/bin/bash',
-      sshkey     => 'AAAAB3NzaC1yc2EAAAADAQABAAABgQDYNp5BTaa0edzbWS9fm99zpI2WG4ZSZn5OZgQ55v9dCOTT9e8iNKI/MRB/ll/Axukun/K0IKdxJjsBq/cmk9RYEvSWdpn8byPQ18FaWpx1eoEuBDPL2EKu7jX09czencgE61dJ8Nli7oO8wt1JBLGVXBgJYV53mPB5XStkBoLj8sNbPBUZpALwNo4DzUBTg0TuT2RzSrDPjnjbOrjJrSC0vs+Ymub0/u1p4DLlW3zR1JUjR6v42mofj6d9XAgZuH8NBWE2kxRt5XKRqYJUK7dvNKocNKc6aENC5W2Te80K2EPHWUWig714cyIh15jehhDxS9G6dezqKM2L4zYxbDCkJXBO1TSdGvcuoWkS+idW6hSCBYmG3YbF5/ZWuhUiFVw4PvyZCQcXGDT7865RSrS8Ba8AsBpXWwh/RNQc818pFIka+p0wsuaebEt1hYIcadQ46YEF+p8KnWSdfrfKv3PZgCXkISR47/WCfWC1frP5/7bpLxSPZbbQXBNZgaB/db8=', # lint:ignore:140chars
-    }
-
     ::users::liu_user { 'simah37':
       commonname => 'Simon Ahrenstedt',
       shell      => '/bin/bash',
diff --git a/metadata.json b/metadata.json
index ff778d798145c5897b7c8a4021dfffdbfc74a853..3791c4b5e7313dde5ed1a2950fbfde79df8f6512 100644
--- a/metadata.json
+++ b/metadata.json
@@ -25,7 +25,7 @@
       "version_requirement": ">= 7.21.0 < 9.0.0"
     }
   ],
-  "pdk-version": "3.0.0",
+  "pdk-version": "3.4.0",
   "template-url": "https://gitlab.it.liu.se/puppet-infra/pdk-templates.git#liu",
-  "template-ref": "heads/liu-0-g73ba36b"
+  "template-ref": "heads/liu-0-g855d974"
 }
diff --git a/spec/default_facts.yml b/spec/default_facts.yml
index f777abfc9905202e7ccf5b5fcc76b9b4c59a39fb..3346c394df5a06fba389c76f1e2ccbb04c77ee6c 100644
--- a/spec/default_facts.yml
+++ b/spec/default_facts.yml
@@ -2,7 +2,8 @@
 #
 # Facts specified here will override the values provided by rspec-puppet-facts.
 ---
-ipaddress: "172.16.254.254"
-ipaddress6: "FE80:0000:0000:0000:AAAA:AAAA:AAAA"
+networking:
+  ip: "172.16.254.254"
+  ip6: "FE80:0000:0000:0000:AAAA:AAAA:AAAA"
+  mac: "AA:AA:AA:AA:AA:AA"
 is_pe: false
-macaddress: "AA:AA:AA:AA:AA:AA"
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 3f9a6c717863bd2eb4bdd13cf9c6b0399fe09129..85b88af530645da8727b70e7687ed73088309393 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -28,7 +28,8 @@ default_fact_files.each do |f|
   next unless File.exist?(f) && File.readable?(f) && File.size?(f)
 
   begin
-    default_facts.merge!(YAML.safe_load(File.read(f), permitted_classes: [], permitted_symbols: [], aliases: true))
+    require 'deep_merge'
+    default_facts.deep_merge!(YAML.safe_load(File.read(f), permitted_classes: [], permitted_symbols: [], aliases: true))
   rescue StandardError => e
     RSpec.configuration.reporter.message "WARNING: Unable to load #{f}: #{e}"
   end
@@ -36,7 +37,7 @@ end
 
 # read default_facts and merge them over what is provided by facterdb
 default_facts.each do |fact, value|
-  add_custom_fact fact, value
+  add_custom_fact fact, value, merge_facts: true
 end
 
 RSpec.configure do |c|