Skip to content

Commit

Permalink
Merge 316a98b into 17c8aa9
Browse files Browse the repository at this point in the history
  • Loading branch information
rico89 committed May 4, 2018
2 parents 17c8aa9 + 316a98b commit f7a6324
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 37 deletions.
94 changes: 72 additions & 22 deletions lib/puppet/provider/gpo/lgpo.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ def exists?
@property_hash[:ensure] == :present
end

def deleted?
@property_hash[:ensure] == :deleted
end

def create
set_value(resource[:value])
@property_hash[:ensure] = :present
Expand Down Expand Up @@ -51,7 +55,7 @@ def self.instances
['machine', 'user'].map do |scope|
pol_file = "C:\\Windows\\System32\\GroupPolicy\\#{scope.capitalize}\\Registry.pol"
next [] unless File.file?(pol_file)

resources = Hash.new

gpos = lgpo('/parse', '/q', "/#{scope[0]}", pol_file)
Expand Down Expand Up @@ -92,10 +96,33 @@ def self.instances
:value => value,
}
end

end
resources.map{|k, v| new(v)}
end.flatten
end.flatten
end

def out_line(scope, key, value_name, value)
"#{scope}\n#{key}\n#{value_name}\n#{value}"
end

# Convert lgpo_import.txt to lgpo_import.pol with lgpo.exe
def convert_to_pol(file)
pol_file = File.basename(file, '.txt') + '.pol'
lgpo_args = ['/r', file, '/w', pol_file]
lgpo(*lgpo_args)
File.delete(file)
pol_file
end

# import lgpo_import.pol with lgpo.exe
def import_pol(file)
lgpo_args = ["/#{scope[0]}", file]
if guid = path['policy_cse']
lgpo_args << '/e' << guid
end
lgpo(*lgpo_args)
File.delete(file)
end

def set_value(val)
Expand All @@ -110,32 +137,55 @@ def set_value(val)
raise Puppet::Error, "Wrong path: '#{path}'"
end

out_scope = scope == 'machine' ? 'computer' : scope

delete_value = setting_valuetype == '[HASHTABLE]' ? 'DELETEALLVALUES' : 'DELETE'
out_scope = (scope == 'machine' ? 'computer' : scope).capitalize

real_val = val == 'DELETE' ? delete_value : "#{path['setting_valuetype'].gsub('REG_', '')}:#{val}"
setting_valuename = real_val == 'DELETEALLVALUES' ? '*' : path['setting_valuename']

out = "#{out_scope}\n#{path['setting_key']}\n#{setting_valuename}\n#{real_val}"
out = Array.new
if setting_valuetype == '[HASHTABLE]'
if val == 'DELETE'
out << out_line(out_scope, path['setting_key'], '*', 'DELETEALLVALUES')
else
val.each do |k, v|
out << out_line(out_scope, path['setting_key'], k, "SZ: #{v}")
end
end
else
val = "#{path['setting_valuetype'].gsub('REG_', '')}:#{val}" unless val == 'DELETE'
out << out_line(out_scope, path['setting_key'], path['setting_valuename'], val)
end

out_file_path = File.join(Puppet[:vardir], 'lgpo_import.txt')
out_polfile_path = File.join(Puppet[:vardir], 'lgpo_import.pol')
File.open(out_file_path, 'w') do |out_file|
out_file.write(out)
out_file.write(out.join("\n\n"))
end

# Convert lgpo_import.txt to lgpo_import.pol with lgpo.exe
lgpo_args = ['/r', out_file_path, '/w', out_polfile_path]
lgpo(*lgpo_args)
File.delete(out_file_path)
remove_key(path['setting_key'], scope) if setting_valuetype == '[HASHTABLE]'

# import lgpo_import.pol with lgpo.exe
lgpo_args = ["/#{scope[0]}", out_polfile_path]
if guid = path['policy_cse']
lgpo_args << '/e' << guid
out_polfile_path = convert_to_pol(out_file_path)

if setting_valuetype == '[HASHTABLE]'
pol_file = "C:\\Windows\\System32\\GroupPolicy\\#{scope.capitalize}\\Registry.pol"
File.delete(pol_file)
end
lgpo(*lgpo_args)
File.delete(out_polfile_path)

import_pol(out_polfile_path)
end

def remove_key(key, scope)
pol_file = "C:\\Windows\\System32\\GroupPolicy\\#{scope.capitalize}\\Registry.pol"
return [] unless File.file?(pol_file)
out_file_path = File.join(Puppet[:vardir], 'lgpo_import.txt')
gpos = lgpo('/parse', '/q', "/#{scope[0]}", pol_file)
# Parse file and remove key
File.open(out_file_path, 'a') do |out_file|
gpos.split("\n\n").reject { |l| l.start_with? ';' }.each do |g|
split_g = g.split("\n")
unless split_g[1] == key
out_file.write("\n\n#{g}")
end
end
end

pol_file = convert_to_pol(out_file)
import_pol(pol_file)
end
end
5 changes: 5 additions & 0 deletions lib/puppet/type/gpo.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
newvalue(:deleted) do
provider.delete
end

def insync?(is)
return true if should == :deleted and provider.deleted?
super
end
end

newparam(:name, :namevar => true) do
Expand Down
91 changes: 76 additions & 15 deletions spec/unit/puppet/provider/gpo/lgpo_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,45 @@ def stub_create(scope, content, cse)
expect(File).to receive(:delete).once.with(out_polfile).and_return(nil)
end

def stub_hash_delete(scope, content, cse)
# This is the initial write to the gpo_import_file which writes the deleteallvalues statement for a hashed instance
lgpo_import_file = StringIO.new
expect(File).to receive(:open).once.with(out_file, 'w').and_yield(lgpo_import_file)
expect(lgpo_import_file).to receive(:write).with(content)

# pol file should get read one time initiated by the lgpo.exe call
pol_file = "C:\\Windows\\System32\\GroupPolicy\\#{scope.capitalize}\\Registry.pol"
allow(File).to receive(:file?) # Catch all calls
expect(File).to receive(:file?).once.with(pol_file).and_return(true)

# the stub for the pol lgpo call needs to be added here in order to simulate output
if true
provider.class.expects(:lgpo).once.with('/parse', '/q', "/#{scope[0]}", pol_file)
.returns(File.read(File.join(
File.dirname(__FILE__),
"../../../../fixtures/unit/puppet/provider/gpo/lgpo/#{scope}/full.out")))
else
provider.class.expects(:lgpo).never
end

# This is the subsequent writes to the lgpo file with the filtered content from the parsing
expect(File).to receive(:open).once.with(out_file, 'a').and_yield(lgpo_import_file)
expect(lgpo_import_file).to receive(:write).at_least(:once)

args = ["/r", out_file]
args << '/w' << out_polfile
provider.class.expects(:lgpo).once.with(*args).returns(nil)
expect(File).to receive(:delete).once.with(out_file).and_return(nil)

# Polfile needs to be deleted so a fresh import can be done from the filtered lgpo file
expect(File).to receive(:delete).once.with(pol_file).and_return(nil)

args = ["/#{scope[0]}", out_polfile]
args << '/e' << cse unless cse.nil?
provider.class.expects(:lgpo).once.with(*args).returns(nil)
expect(File).to receive(:delete).once.with(out_polfile).and_return(nil)
end

context 'when listing instances' do
context 'when the gpo file exists' do
it 'should list instances' do
Expand Down Expand Up @@ -175,7 +214,7 @@ def stub_create(scope, content, cse)

context 'when there is no cse' do
it 'should create a resource without /e' do
stub_create('machine', "computer\nSoftware\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU\nAllowMUUpdateService\nDWORD:1", nil)
stub_create('machine', "Computer\nSoftware\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU\nAllowMUUpdateService\nDWORD:1", nil)

provider.create
end
Expand All @@ -191,8 +230,25 @@ def stub_create(scope, content, cse)
end

it 'should create a resource with /e' do
stub_create('machine', "computer\nSoftware\\Policies\\Microsoft Services\\AdmPwd\nAdmPwdEnabled\nDWORD:1", '{D76B9641-3288-4f75-942D-087DE603E3EA}')
stub_create('machine', "Computer\nSoftware\\Policies\\Microsoft Services\\AdmPwd\nAdmPwdEnabled\nDWORD:1", '{D76B9641-3288-4f75-942D-087DE603E3EA}')

provider.create
end
end

context 'when resource contain a hash value' do
let(:params) do
{
:title => 'windowsdefender::exclusions_processes::exclusions_processeslist',
:value => {'c:\windows\process0.exe' => '0', 'c:\windows\process1.exe' => '0',},
:provider => 'lgpo',
}
end

it 'should create two entries in LGPO import file' do
stub_create('machine', "Computer\nSoftware\\Policies\\Microsoft\\Windows Defender\\Exclusions\\Processes\nc:\\windows\\process0.exe\nSZ:0\n\nComputer\nSoftware\\Policies\\Microsoft\\Windows Defender\\Exclusions\\Processes\nc:\\windows\\process1.exe\nSZ:0", nil)
pol_file = "C:\\Windows\\System32\\GroupPolicy\\Machine\\Registry.pol"
expect(File).to receive(:delete).once.with(pol_file).and_return(nil)
provider.create
end
end
Expand All @@ -205,38 +261,43 @@ def stub_create(scope, content, cse)

context 'when there is no cse' do
it 'should create a resource without /e' do
stub_create('machine', "computer\nSoftware\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU\nAllowMUUpdateService\nDELETE", nil)
stub_create('machine', "Computer\nSoftware\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU\nAllowMUUpdateService\nDELETE", nil)

provider.delete
end
end

context 'when we need to delete a HASHTABLE instance' do
context 'when there is a cse' do
let(:params) do
{
:title => 'machine::windowsdefender::exclusions_processes::exclusions_processeslist',
:ensure => :deleted,
:title => 'admpwd::pol_admpwd_enabled::admpwdenabled',
:value => '1',
:provider => 'lgpo',
}
end
it 'should create a resource without /e' do
stub_create('machine', "computer\nSoftware\\Policies\\Microsoft\\Windows Defender\\Exclusions\\Processes\n*\nDELETEALLVALUES", nil)

it 'should create a resource with /e' do
stub_create('machine', "Computer\nSoftware\\Policies\\Microsoft Services\\AdmPwd\nAdmPwdEnabled\nDELETE", '{D76B9641-3288-4f75-942D-087DE603E3EA}')

provider.delete
end
end

context 'when there is a cse' do
end
context 'when deleting a hash resource' do
before :each do
expect(Puppet).to receive(:[]).exactly(3).times.with(:vardir).and_return('C:\ProgramData\PuppetLabs\Puppet\var')
end

context 'when we need to delete a HASHTABLE instance' do
let(:params) do
{
:title => 'admpwd::pol_admpwd_enabled::admpwdenabled',
:value => '1',
:title => 'machine::windowsdefender::exclusions_processes::exclusions_processeslist',
:ensure => :deleted,
:provider => 'lgpo',
}
end

it 'should create a resource with /e' do
stub_create('machine', "computer\nSoftware\\Policies\\Microsoft Services\\AdmPwd\nAdmPwdEnabled\nDELETE", '{D76B9641-3288-4f75-942D-087DE603E3EA}')
it 'should create a resource without /e' do
stub_hash_delete('machine', "Computer\nSoftware\\Policies\\Microsoft\\Windows Defender\\Exclusions\\Processes\n*\nDELETEALLVALUES", nil)

provider.delete
end
Expand Down

0 comments on commit f7a6324

Please sign in to comment.