diff --git a/moduleroot/Gemfile.erb b/moduleroot/Gemfile.erb
index ecaf7da939f26279b8b69457c95b32d57cb45dbb..d7fb69ea8d9bdad0b92d0c59c41bcf9cc9a1363e 100644
--- a/moduleroot/Gemfile.erb
+++ b/moduleroot/Gemfile.erb
@@ -58,16 +58,31 @@ end
 -%>
 source ENV['GEM_SOURCE'] || 'https://rubygems.org'
 
-def location_for(place_or_version, fake_version = nil)
+def location_for(place_or_version, fake_version = nil, gem_name = nil)
   git_url_regex = %r{\A(?<url>(https?|git)[:@][^#]*)(#(?<branch>.*))?}
   file_url_regex = %r{\Afile:\/\/(?<path>.*)}
 
-  if place_or_version && (git_url = place_or_version.match(git_url_regex))
+  version = place_or_version || '>= 0'
+
+  # Configure private source for puppet-related gems when auth token is available
+  is_puppet_gem = ['puppet', 'facter'].include?(gem_name)
+  has_auth_token = !ENV['PUPPET_AUTH_TOKEN'].to_s.empty?
+
+  if is_puppet_gem && has_auth_token
+    # When using puppet gem with private source and no specific version requested,
+    # enforce the version constraint
+    if gem_name == 'puppet' && version == '>= 0'
+      version = '~> 8.11'
+    end
+
+    [version, { require: false, source: 'https://rubygems-puppetcore.puppet.com' }]
+  elsif place_or_version && (git_url = place_or_version.match(git_url_regex))
     [fake_version, { git: git_url[:url], branch: git_url[:branch], require: false }].compact
   elsif place_or_version && (file_url = place_or_version.match(file_url_regex))
     ['>= 0', { path: File.expand_path(file_url[:path]), require: false }]
   else
-    [place_or_version, { require: false }]
+    # Default to rubygems.org when PUPPET_AUTH_TOKEN is not set
+    [version, { require: false }]
   end
 end
 
@@ -110,13 +125,11 @@ hiera_version = ENV['HIERA_GEM_VERSION']
 
 gems = {}
 
-gems['puppet'] = location_for(puppet_version)
-
-# If facter or hiera versions have been specified via the environment
-# variables
+gems['puppet'] = location_for(puppet_version, nil, 'puppet')
 
-gems['facter'] = location_for(facter_version) if facter_version
-gems['hiera'] = location_for(hiera_version) if hiera_version
+# If facter or hiera versions have been specified via the environment variables
+gems['facter'] = location_for(facter_version, nil, 'facter')
+gems['hiera'] = location_for(hiera_version, nil, 'hiera') if hiera_version
 
 gems.each do |gem_name, gem_params|
   gem gem_name, *gem_params
diff --git a/moduleroot/spec/support/gemfile_spec.rb.erb b/moduleroot/spec/support/gemfile_spec.rb.erb
new file mode 100644
index 0000000000000000000000000000000000000000..12db74265ce7693763d07e55fa5e6c3c597c1886
--- /dev/null
+++ b/moduleroot/spec/support/gemfile_spec.rb.erb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require 'bundler'
+
+RSpec.describe 'Gemfile.lock verification' do
+  let(:parser) { Bundler::LockfileParser.new(Bundler.read_file(Bundler.default_lockfile)) }
+  let(:private_source) { 'https://rubygems-puppetcore.puppet.com/' }
+  let(:public_source) { 'https://rubygems.org/' }
+  let(:auth_token_present?) { !ENV['PUPPET_AUTH_TOKEN'].nil? }
+
+  # Helper method to get source remotes for a specific gem
+  def get_gem_source_remotes(gem_name)
+    spec = parser.specs.find { |s| s.name == gem_name }
+    return [] unless spec
+
+    source = spec.source
+    return [] unless source.is_a?(Bundler::Source::Rubygems)
+    
+    source.remotes.map(&:to_s)
+  end
+
+  context 'when PUPPET_AUTH_TOKEN is present' do
+    before do
+      skip "Skipping private source tests - PUPPET_AUTH_TOKEN not present" unless auth_token_present?
+    end
+
+    it 'has puppet under private source' do
+      remotes = get_gem_source_remotes('puppet')
+      expect(remotes).to eq([private_source]), 
+        "Expected puppet to use private source #{private_source}, got: #{remotes.join(', ')}"
+      expect(remotes).not_to eq([public_source]), 
+        "Expected puppet to not use public source #{public_source}, got: #{remotes.join(', ')}"
+    end
+
+    it 'has facter under private source' do
+      remotes = get_gem_source_remotes('facter')
+      expect(remotes).to eq([private_source]), 
+        "Expected facter to use private source #{private_source}, got: #{remotes.join(', ')}"
+      expect(remotes).not_to eq([public_source]), 
+        "Expected facter to not use public source #{public_source}, got: #{remotes.join(', ')}"
+    end
+  end
+
+  context 'when PUPPET_AUTH_TOKEN is not present' do
+    before do
+      skip "Skipping public source tests - PUPPET_AUTH_TOKEN is present" if auth_token_present?
+    end
+
+    it 'has puppet under public source' do
+      remotes = get_gem_source_remotes('puppet')
+      expect(remotes).to eq([public_source]), 
+        "Expected puppet to use public source #{public_source}, got: #{remotes.join(', ')}"
+      expect(remotes).not_to eq([private_source]), 
+        "Expected puppet to not use private source #{private_source}, got: #{remotes.join(', ')}"
+    end
+
+    it 'has facter under public source' do
+      remotes = get_gem_source_remotes('facter')
+      expect(remotes).to eq([public_source]), 
+        "Expected facter to use public source #{public_source}, got: #{remotes.join(', ')}"
+      expect(remotes).not_to eq([private_source]), 
+        "Expected facter to not use private source #{private_source}, got: #{remotes.join(', ')}"
+    end
+  end
+end