diff --git a/Gemfile.lock b/Gemfile.lock
index 1d2fd36da..e70c47495 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -91,9 +91,9 @@ GEM
nori (2.6.0)
ovirt-engine-sdk (4.2.4)
json (>= 1, < 3)
- packetfu (1.1.11)
- pcaprub (~> 0.12)
- pcaprub (0.12.4)
+ packetfu (1.1.13)
+ pcaprub
+ pcaprub (0.13.0)
pg (1.1.3)
process_helper (0.1.2)
puppet (6.0.0)
@@ -186,4 +186,4 @@ DEPENDENCIES
zipruby
BUNDLED WITH
- 1.16.1
+ 2.0.1
diff --git a/README.md b/README.md
index 01edc3281..8279482f1 100644
--- a/README.md
+++ b/README.md
@@ -51,6 +51,16 @@ cd /home/user/bin/SecGen
bundle install
```
+To use the Windows basesboxes you will need to install Packer. Use the following command:
+```bash
+curl -SL https://releases.hashicorp.com/packer/1.3.2/packer_1.3.2_linux_amd64.zip -o packer_1.3.2_linux_amd64.zip
+unzip packer_1.3.2_linux_amd64.zip
+sudo mv packer /usr/local/
+sudo bash -c 'echo "export PATH=\"\$PATH:/usr/local/\"" >> /etc/environment'
+sudo vagrant plugin install winrm
+sudo vagrant plugin install winrm-fs
+```
+
## Usage
Basic usage:
```bash
diff --git a/lib/templates/Vagrantfile.erb b/lib/templates/Vagrantfile.erb
index d4f6017fd..02d9641f0 100644
--- a/lib/templates/Vagrantfile.erb
+++ b/lib/templates/Vagrantfile.erb
@@ -202,11 +202,12 @@ end
}
<% end -%>
<%=module_name%>.module_path = "<%="puppet/#{system.name}/modules"%>"
- <%=module_name%>.environment_path = "environments/"
- <%=module_name%>.environment = "production"
-
- <%=module_name%>.synced_folder_type = "rsync"
- <%=module_name%>.manifests_path = "<%="puppet/#{system.name}/modules/#{selected_module.module_path_end}"%>"
+ <% if selected_module.attributes['platform'].first.downcase != 'windows' %>
+ <%=module_name%>.environment_path = "environments/"
+ <%=module_name%>.environment = "production"
+ <%=module_name%>.synced_folder_type = "rsync"
+ <% end %>
+ <%=module_name%>.manifests_path = "<%="puppet/#{system.name}/modules/#{selected_module.module_path_end}"%>"
<%=module_name%>.manifest_file = "<%="#{selected_module.module_path_end}.pp"%>"
end
<% end -%>
diff --git a/modules/bases/windows_server_2008_r2_amd64/Autounattend.xml b/modules/bases/windows_server_2008_r2_amd64/Autounattend.xml
new file mode 100644
index 000000000..342c4bbdb
--- /dev/null
+++ b/modules/bases/windows_server_2008_r2_amd64/Autounattend.xml
@@ -0,0 +1,305 @@
+
+
+
+
+
+
+
+
+
+ 1
+ Primary
+ true
+
+
+
+
+ false
+ NTFS
+ C
+ 1
+ 1
+
+
+
+ 0
+ true
+
+ OnError
+
+
+ true
+ Vagrant Administrator
+ Vagrant Inc.
+
+
+
+
+
+
+
+ Never
+
+
+
+
+
+ 0
+ 1
+
+ OnError
+ false
+
+
+ /IMAGE/NAME
+ Windows Server 2008 R2 SERVERSTANDARD
+
+
+
+
+
+
+
+ en-US
+
+ en-US
+ en-US
+ en-US
+ en-US
+ en-US
+
+
+
+
+ false
+
+
+
+
+
+
+ vagrant
+ true
+
+
+
+
+ vagrant
+ true
+
+ Vagrant User
+ vagrant
+ administrators
+ vagrant
+
+
+
+
+ true
+ true
+ Home
+
+
+
+ vagrant
+ true
+
+ vagrant
+ true
+
+
+
+ cmd.exe /c powershell -Command "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force"
+ Set Execution Policy 64 Bit
+ 1
+ true
+
+
+ C:\Windows\SysWOW64\cmd.exe /c powershell -Command "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force"
+ Set Execution Policy 32 Bit
+ 2
+ true
+
+
+ cmd.exe /c winrm quickconfig -q
+ winrm quickconfig -q
+ 3
+ true
+
+
+ cmd.exe /c winrm quickconfig -transport:http
+ winrm quickconfig -transport:http
+ 4
+ true
+
+
+ cmd.exe /c winrm set winrm/config @{MaxTimeoutms="1800000"}
+ Win RM MaxTimoutms
+ 5
+ true
+
+
+ cmd.exe /c winrm set winrm/config/winrs @{MaxMemoryPerShellMB="800"}
+ Win RM MaxMemoryPerShellMB
+ 6
+ true
+
+
+ cmd.exe /c winrm set winrm/config/winrs @{MaxShellsPerUser="999"}
+ Win RM MaxShellsPerUser
+ 7
+ true
+
+
+ cmd.exe /c winrm set winrm/config/winrs @{MaxProcessesPerShell="999"}
+ Win RM MaxProcessesPerShell
+ 8
+ true
+
+
+ cmd.exe /c winrm set winrm/config/service @{MaxConcurrentOperationsPerUser="999"}
+ Win RM ConcurrentOperationsPerUser
+ 9
+ true
+
+
+ cmd.exe /c winrm set winrm/config/service @{AllowUnencrypted="true"}
+ Win RM AllowUnencrypted
+ 10
+ true
+
+
+ cmd.exe /c winrm set winrm/config/service/auth @{Basic="true"}
+ Win RM auth Basic
+ 11
+ true
+
+
+ cmd.exe /c winrm set winrm/config/client/auth @{Basic="true"}
+ Win RM client auth Basic
+ 12
+ true
+
+
+ cmd.exe /c winrm set winrm/config/listener?Address=*+Transport=HTTP @{Port="5985"}
+ Win RM listener Address/Port
+ 13
+ true
+
+
+ cmd.exe /c netsh advfirewall firewall set rule group="remote administration" new enable=yes
+ Win RM adv firewall enable
+ 14
+ true
+
+
+ cmd.exe /c netsh firewall add portopening TCP 5985 "Port 5985"
+ Win RM port open
+ 15
+ true
+
+
+ cmd.exe /c net stop winrm
+ Stop Win RM Service
+ 16
+ true
+
+
+ cmd.exe /c sc config winrm start= auto
+ Win RM Autostart
+ 17
+ true
+
+
+ cmd.exe /c net start winrm
+ Start Win RM Service
+ 18
+ true
+
+
+ %SystemRoot%\System32\reg.exe ADD HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\ /v HideFileExt /t REG_DWORD /d 0 /f
+ 19
+ Show file extensions in Explorer
+
+
+ %SystemRoot%\System32\reg.exe ADD HKCU\Console /v QuickEdit /t REG_DWORD /d 1 /f
+ 20
+ Enable QuickEdit mode
+
+
+ %SystemRoot%\System32\reg.exe ADD HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\ /v Start_ShowRun /t REG_DWORD /d 1 /f
+ 21
+ Show Run command in Start Menu
+
+
+ %SystemRoot%\System32\reg.exe ADD HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\ /v StartMenuAdminTools /t REG_DWORD /d 1 /f
+ 22
+ Show Administrative Tools in Start Menu
+
+
+ %SystemRoot%\System32\reg.exe ADD HKLM\SYSTEM\CurrentControlSet\Control\Power\ /v HibernateFileSizePercent /t REG_DWORD /d 0 /f
+ 23
+ Zero Hibernation File
+
+
+ %SystemRoot%\System32\reg.exe ADD HKLM\SYSTEM\CurrentControlSet\Control\Power\ /v HibernateEnabled /t REG_DWORD /d 0 /f
+ 24
+ Disable Hibernation Mode
+
+
+ cmd.exe /c wmic useraccount where "name='vagrant'" set PasswordExpires=FALSE
+ 25
+ Disable password expiration for vagrant user
+
+
+
+ cmd.exe /c C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -File a:\openssh.ps1 -AutoStart
+ Install OpenSSH
+ 99
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+ false
+
+
+ vagrant-2008R2
+ Pacific Standard Time
+
+
+
+ true
+
+
+
+ false
+ false
+
+
+ true
+
+
+ true
+
+
+
+
diff --git a/modules/bases/windows_server_2008_r2_amd64/Packerfile b/modules/bases/windows_server_2008_r2_amd64/Packerfile
new file mode 100644
index 000000000..a1005ad9d
--- /dev/null
+++ b/modules/bases/windows_server_2008_r2_amd64/Packerfile
@@ -0,0 +1,56 @@
+{
+"builders": [{
+
+ "type": "virtualbox-iso",
+ "vboxmanage": [
+
+
+
+ [ "modifyvm", "{{.Name}}", "--natpf1", "winrm,tcp,,5985,,5985" ]
+ ],
+ "guest_os_type": "Windows2008_64",
+
+
+"headless": true,
+"disk_size": 61440,
+"iso_url": "http://download.microsoft.com/download/7/5/E/75EC4E54-5B02-42D6-8879-D8D3A25FBEF7/7601.17514.101119-1850_x64fre_server_eval_en-us-GRMSXEVAL_EN_DVD.iso",
+"iso_checksum": "4263be2cf3c59177c45085c0a7bc6ca5",
+"iso_checksum_type": "md5",
+"communicator": "winrm",
+"winrm_username": "vagrant",
+"winrm_password": "vagrant",
+"winrm_port": "5985",
+"winrm_timeout": "5h",
+"shutdown_command": "shutdown /s /t 10 /f /d p:4:1 /c \"Packer Shutdown\"",
+"shutdown_timeout": "1h",
+"floppy_files": [
+"Autounattend.xml"
+]
+}],
+"provisioners": [
+{
+"type": "windows-shell",
+"script": "scripts/vm-guest-tools.bat",
+"execute_command": "{{ .Vars }} cmd /c {{ .Path }}"
+},
+{
+"type": "powershell",
+"environment_vars": "PuppetVersion=3.8.7",
+"script": "puppet_install/windows.ps1"
+}
+],
+"post-processors": [
+{
+"type": "vagrant",
+"keep_input_artifact": false,
+"override": {
+"virtualbox": {
+"output": "/home/laptop02/bin/SecGen/.generated/windows_2008_64_virtualbox.box"
+},
+"vmware": {
+"output": "/home/laptop02/bin/SecGen/.generated/windows_2008_64_vmware_esxi.box"
+}
+}
+}
+]
+}
diff --git a/modules/bases/windows_server_2008_r2_amd64/puppet_install/windows.ps1 b/modules/bases/windows_server_2008_r2_amd64/puppet_install/windows.ps1
index 2a8cacf69..f554e7ec7 100644
--- a/modules/bases/windows_server_2008_r2_amd64/puppet_install/windows.ps1
+++ b/modules/bases/windows_server_2008_r2_amd64/puppet_install/windows.ps1
@@ -21,8 +21,7 @@
This defaults to $null.
#>
param(
- # [string]$MsiUrl = "https://downloads.puppetlabs.com/windows/puppet-3.3.2.msi"
- [string]$MsiUrl = "https://downloads.puppetlabs.com/windows/puppet-3.8.7.msi"
+ [string]$MsiUrl = "https://downloads.puppetlabs.com/windows/puppet6/puppet-agent-x64-latest.msi"
,[string]$PuppetVersion = $null
)
@@ -51,7 +50,7 @@ if (!($PuppetInstalled)) {
}
# Install it - msiexec will download from the url
- $install_args = @("/qn", "/norestart","/i", $MsiUrl)
+ $install_args = @("/i", $MsiUrl,"/qn", "/norestart")
Write-Host "Installing Puppet. Running msiexec.exe $install_args"
$process = Start-Process -FilePath msiexec.exe -ArgumentList $install_args -Wait -PassThru
if ($process.ExitCode -ne 0) {
diff --git a/modules/build/puppet/secgen_functions/manifests/leak_file.pp b/modules/build/puppet/secgen_functions/manifests/leak_file.pp
index dd1b35068..5cb3a90d7 100644
--- a/modules/build/puppet/secgen_functions/manifests/leak_file.pp
+++ b/modules/build/puppet/secgen_functions/manifests/leak_file.pp
@@ -3,27 +3,59 @@ define secgen_functions::leak_file($leaked_filename, $storage_directory, $string
$path_to_leak = "$storage_directory/$leaked_filename"
# create the directory tree, incase the file name has extra layers of directories
- exec { "$leaked_from-$path_to_leak-mkdir":
- path => ['/bin', '/usr/bin', '/usr/local/bin', '/sbin', '/usr/sbin'],
- command => "mkdir -p `dirname $path_to_leak`;chown $owner. `dirname $path_to_leak`",
- provider => shell,
+ if $::osfamily == 'windows' {
+ exec { "win_$leaked_from-$path_to_leak-mkdir":
+ command => "cmd.exe /c md $storage_directory",
+ path => "C:\\Windows\\System32",
+ returns => [1]
+ } ->
+ exec { "win_$leaked_from-$path_to_leak-takeown":
+ command => "cmd.exe /c takeown /F $storage_directory /U $owner",
+ path => "C:\\Windows\\System32",
+ returns => [1]
+ }
+ # assuming everything not windows has mkdir and chown
+ } else {
+ exec { "$leaked_from-$path_to_leak-mkdir":
+ path => ['/bin', '/usr/bin', '/usr/local/bin', '/sbin', '/usr/sbin'],
+ command => "mkdir -p `dirname $path_to_leak`;chown $owner. `dirname $path_to_leak`",
+ provider => shell,
+ }
+
}
# If the file already exists append to it, otherwise create it.
- if (defined(File[$path_to_leak])){
- notice("File with that name already defined, appending leaked strings instead...")
- exec { "$leaked_from-$path_to_leak-append":
- path => ['/bin', '/usr/bin', '/usr/local/bin', '/sbin', '/usr/sbin'],
- command => "echo \"\n------\n$strings_to_leak\" >> $path_to_leak",
- }
- } else {
- file { $path_to_leak:
- ensure => present,
- owner => $owner,
- group => $group,
- mode => $mode,
- content => template('secgen_functions/overshare.erb')
- }
- }
+ if $::osfamily == 'windows' {
+ if (defined(File[$path_to_leak])){
+ notice("File with that name already defined, appending leaked strings instead...")
+ file_line { "$leaked_from-$path_to_leak -append":
+ path => $path_to_leak ,
+ line => "\n------\n$strings_to_leak",
+ }
+ } else {
+ file { $path_to_leak:
+ ensure => present,
+ owner => $owner,
+ group => $group,
+ content => template('secgen_functions/overshare.erb')
+ }
+ }
+ }else{ # If the file already exists append to it, otherwise create it.
+ if (defined(File[$path_to_leak])){
+ notice("File with that name already defined, appending leaked strings instead...")
+ file_line { "$leaked_from-$path_to_leak -append":
+ path => $path_to_leak ,
+ line => "\n------\n$strings_to_leak",
+ }
+ } else {
+ file { $path_to_leak:
+ ensure => present,
+ owner => $owner,
+ group => $group,
+ mode => $mode,
+ content => template('secgen_functions/overshare.erb')
+ }
+ }
+ }
}
}
diff --git a/modules/build/puppet/secgen_functions/manifests/leak_files.pp b/modules/build/puppet/secgen_functions/manifests/leak_files.pp
index e2abe595b..73a33f55b 100644
--- a/modules/build/puppet/secgen_functions/manifests/leak_files.pp
+++ b/modules/build/puppet/secgen_functions/manifests/leak_files.pp
@@ -17,6 +17,7 @@ define secgen_functions::leak_files($leaked_filenames=[], $storage_directory, $s
storage_directory => $storage_directory,
strings_to_leak => $leaked_strings,
owner => $owner,
+ group => $group,
mode => $mode,
}
} else {
@@ -28,6 +29,7 @@ define secgen_functions::leak_files($leaked_filenames=[], $storage_directory, $s
storage_directory => $storage_directory,
strings_to_leak => $leaked_strings,
owner => $owner,
+ group => $group,
mode => $mode,
leaked_from => $leaked_file_resource, # pass this in when appending to avoid resource clashes
}
diff --git a/modules/utilities/unix/system/parameterised_accounts/manifests/init.pp b/modules/utilities/unix/system/parameterised_accounts/manifests/init.pp
index 680b58156..a8da911ba 100644
--- a/modules/utilities/unix/system/parameterised_accounts/manifests/init.pp
+++ b/modules/utilities/unix/system/parameterised_accounts/manifests/init.pp
@@ -16,4 +16,4 @@ class parameterised_accounts::init {
}
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/utilities/windows/system/accounts/CHANGELOG.md b/modules/utilities/windows/system/accounts/CHANGELOG.md
new file mode 100755
index 000000000..d7ace31d4
--- /dev/null
+++ b/modules/utilities/windows/system/accounts/CHANGELOG.md
@@ -0,0 +1,25 @@
+# Change log
+All notable changes to this project will be documented in this file.
+
+## Supported Release 1.1.0
+### Summary
+A feature rich release, with the addition of Debian 8 support. Also several generic fixes to tests.
+
+#### Features
+- Now allows SSH keys to be purged from user.
+- Multiple updates and fixes to the README.
+- RSpec-puppet has now been unpinned.
+- Addition of Debian 8 compatibility to metadata.
+- Addition of OSfamily fact to tests.
+- Several modulesync updates.
+
+#### Bugfixes
+- Multiple fixes to tests.
+
+## Supported Release 1.0.0
+### Summary:
+This is the initial release of the rewrite of puppetlabs-pe\_accounts for a more general usage.
+
+Differences from the pe\_accounts module is that the data model is gone, and thus the base class that accepts hashes (ie, from hiera). Instead, the module is designed around the use of the `accounts::user` defined resource.
+
+To regain the old hiera behavior, use the `create_resources()` function in combination with `accounts::user`; eg: `create_resources('accounts::user', hiera_hash('accounts::users'))`
diff --git a/modules/utilities/windows/system/accounts/CONTRIBUTING.md b/modules/utilities/windows/system/accounts/CONTRIBUTING.md
new file mode 100644
index 000000000..3c3f1e799
--- /dev/null
+++ b/modules/utilities/windows/system/accounts/CONTRIBUTING.md
@@ -0,0 +1,218 @@
+Checklist (and a short version for the impatient)
+=================================================
+
+ * Commits:
+
+ - Make commits of logical units.
+
+ - Check for unnecessary whitespace with "git diff --check" before
+ committing.
+
+ - Commit using Unix line endings (check the settings around "crlf" in
+ git-config(1)).
+
+ - Do not check in commented out code or unneeded files.
+
+ - The first line of the commit message should be a short
+ description (50 characters is the soft limit, excluding ticket
+ number(s)), and should skip the full stop.
+
+ - Associate the issue in the message. The first line should include
+ the issue number in the form "(#XXXX) Rest of message".
+
+ - The body should provide a meaningful commit message, which:
+
+ - uses the imperative, present tense: "change", not "changed" or
+ "changes".
+
+ - includes motivation for the change, and contrasts its
+ implementation with the previous behavior.
+
+ - Make sure that you have tests for the bug you are fixing, or
+ feature you are adding.
+
+ - Make sure the test suites passes after your commit:
+ `bundle exec rspec spec/acceptance` More information on [testing](#Testing) below
+
+ - When introducing a new feature, make sure it is properly
+ documented in the README.md
+
+ * Submission:
+
+ * Pre-requisites:
+
+ - Make sure you have a [GitHub account](https://github.com/join)
+
+ - [Create a ticket](https://tickets.puppetlabs.com/secure/CreateIssue!default.jspa), or [watch the ticket](https://tickets.puppetlabs.com/browse/) you are patching for.
+
+ * Preferred method:
+
+ - Fork the repository on GitHub.
+
+ - Push your changes to a topic branch in your fork of the
+ repository. (the format ticket/1234-short_description_of_change is
+ usually preferred for this project).
+
+ - Submit a pull request to the repository in the puppetlabs
+ organization.
+
+The long version
+================
+
+ 1. Make separate commits for logically separate changes.
+
+ Please break your commits down into logically consistent units
+ which include new or changed tests relevant to the rest of the
+ change. The goal of doing this is to make the diff easier to
+ read for whoever is reviewing your code. In general, the easier
+ your diff is to read, the more likely someone will be happy to
+ review it and get it into the code base.
+
+ If you are going to refactor a piece of code, please do so as a
+ separate commit from your feature or bug fix changes.
+
+ We also really appreciate changes that include tests to make
+ sure the bug is not re-introduced, and that the feature is not
+ accidentally broken.
+
+ Describe the technical detail of the change(s). If your
+ description starts to get too long, that is a good sign that you
+ probably need to split up your commit into more finely grained
+ pieces.
+
+ Commits which plainly describe the things which help
+ reviewers check the patch and future developers understand the
+ code are much more likely to be merged in with a minimum of
+ bike-shedding or requested changes. Ideally, the commit message
+ would include information, and be in a form suitable for
+ inclusion in the release notes for the version of Puppet that
+ includes them.
+
+ Please also check that you are not introducing any trailing
+ whitespace or other "whitespace errors". You can do this by
+ running "git diff --check" on your changes before you commit.
+
+ 2. Sending your patches
+
+ To submit your changes via a GitHub pull request, we _highly_
+ recommend that you have them on a topic branch, instead of
+ directly on "master".
+ It makes things much easier to keep track of, especially if
+ you decide to work on another thing before your first change
+ is merged in.
+
+ GitHub has some pretty good
+ [general documentation](http://help.github.com/) on using
+ their site. They also have documentation on
+ [creating pull requests](http://help.github.com/send-pull-requests/).
+
+ In general, after pushing your topic branch up to your
+ repository on GitHub, you can switch to the branch in the
+ GitHub UI and click "Pull Request" towards the top of the page
+ in order to open a pull request.
+
+
+ 3. Update the related GitHub issue.
+
+ If there is a GitHub issue associated with the change you
+ submitted, then you should update the ticket to include the
+ location of your branch, along with any other commentary you
+ may wish to make.
+
+Testing
+=======
+
+Getting Started
+---------------
+
+Our puppet modules provide [`Gemfile`](./Gemfile)s which can tell a ruby
+package manager such as [bundler](http://bundler.io/) what Ruby packages,
+or Gems, are required to build, develop, and test this software.
+
+Please make sure you have [bundler installed](http://bundler.io/#getting-started)
+on your system, then use it to install all dependencies needed for this project,
+by running
+
+```shell
+% bundle install
+Fetching gem metadata from https://rubygems.org/........
+Fetching gem metadata from https://rubygems.org/..
+Using rake (10.1.0)
+Using builder (3.2.2)
+-- 8><-- many more --><8 --
+Using rspec-system-puppet (2.2.0)
+Using serverspec (0.6.3)
+Using rspec-system-serverspec (1.0.0)
+Using bundler (1.3.5)
+Your bundle is complete!
+Use `bundle show [gemname]` to see where a bundled gem is installed.
+```
+
+NOTE some systems may require you to run this command with sudo.
+
+If you already have those gems installed, make sure they are up-to-date:
+
+```shell
+% bundle update
+```
+
+With all dependencies in place and up-to-date we can now run the tests:
+
+```shell
+% bundle exec rake spec
+```
+
+This will execute all the [rspec tests](http://rspec-puppet.com/) tests
+under [spec/defines](./spec/defines), [spec/classes](./spec/classes),
+and so on. rspec tests may have the same kind of dependencies as the
+module they are testing. While the module defines in its [Modulefile](./Modulefile),
+rspec tests define them in [.fixtures.yml](./fixtures.yml).
+
+Some puppet modules also come with [beaker](https://github.com/puppetlabs/beaker)
+tests. These tests spin up a virtual machine under
+[VirtualBox](https://www.virtualbox.org/)) with, controlling it with
+[Vagrant](http://www.vagrantup.com/) to actually simulate scripted test
+scenarios. In order to run these, you will need both of those tools
+installed on your system.
+
+You can run them by issuing the following command
+
+```shell
+% bundle exec rake spec_clean
+% bundle exec rspec spec/acceptance
+```
+
+This will now download a pre-fabricated image configured in the [default node-set](./spec/acceptance/nodesets/default.yml),
+install puppet, copy this module and install its dependencies per [spec/spec_helper_acceptance.rb](./spec/spec_helper_acceptance.rb)
+and then run all the tests under [spec/acceptance](./spec/acceptance).
+
+Writing Tests
+-------------
+
+XXX getting started writing tests.
+
+If you have commit access to the repository
+===========================================
+
+Even if you have commit access to the repository, you will still need to
+go through the process above, and have someone else review and merge
+in your changes. The rule is that all changes must be reviewed by a
+developer on the project (that did not write the code) to ensure that
+all changes go through a code review process.
+
+Having someone other than the author of the topic branch recorded as
+performing the merge is the record that they performed the code
+review.
+
+
+Additional Resources
+====================
+
+* [Getting additional help](http://puppet.com/community/get-help)
+
+* [Writing tests](https://docs.puppet.com/guides/module_guides/bgtm.html#step-three-module-testing)
+
+* [General GitHub documentation](http://help.github.com/)
+
+* [GitHub pull request documentation](http://help.github.com/send-pull-requests/)
+
diff --git a/modules/utilities/windows/system/accounts/Gemfile b/modules/utilities/windows/system/accounts/Gemfile
new file mode 100644
index 000000000..c97275bd8
--- /dev/null
+++ b/modules/utilities/windows/system/accounts/Gemfile
@@ -0,0 +1,48 @@
+#This file is generated by ModuleSync, do not edit.
+
+source ENV['GEM_SOURCE'] || "https://rubygems.org"
+
+def location_from_env(env, default_location = [])
+ if location = ENV[env]
+ if location =~ /^((?:git|https?)[:@][^#]*)#(.*)/
+ [{ :git => $1, :branch => $2, :require => false }]
+ elsif location =~ /^file:\/\/(.*)/
+ ['>= 0', { :path => File.expand_path($1), :require => false }]
+ else
+ [location, { :require => false }]
+ end
+ else
+ default_location
+ end
+end
+
+group :development, :unit_tests do
+ gem 'metadata-json-lint'
+ gem 'puppet_facts'
+ gem 'puppet-blacksmith', '>= 3.4.0'
+ gem 'puppetlabs_spec_helper', '>= 1.2.1'
+ gem 'rspec-puppet', '>= 2.3.2'
+ gem 'rspec-puppet-facts'
+ gem 'simplecov'
+ gem 'parallel_tests'
+ gem 'rubocop', '0.41.2' if RUBY_VERSION < '2.0.0'
+ gem 'rubocop' if RUBY_VERSION >= '2.0.0'
+ gem 'rubocop-rspec', '~> 1.6' if RUBY_VERSION >= '2.3.0'
+ gem 'json_pure', '<= 2.0.1' if RUBY_VERSION < '2.0.0'
+end
+group :system_tests do
+ gem 'beaker', *location_from_env('BEAKER_VERSION', []) if RUBY_VERSION >= '2.3.0'
+ gem 'beaker', *location_from_env('BEAKER_VERSION', ['< 3']) if RUBY_VERSION < '2.3.0'
+ gem 'beaker-rspec', *location_from_env('BEAKER_RSPEC_VERSION', ['>= 3.4'])
+ gem 'serverspec'
+ gem 'beaker-puppet_install_helper'
+ gem 'master_manipulator'
+ gem 'beaker-hostgenerator', *location_from_env('BEAKER_HOSTGENERATOR_VERSION', [])
+end
+
+gem 'facter', *location_from_env('FACTER_GEM_VERSION')
+gem 'puppet', *location_from_env('PUPPET_GEM_VERSION')
+
+if File.exists? "#{__FILE__}.local"
+ eval(File.read("#{__FILE__}.local"), binding)
+end
diff --git a/modules/utilities/windows/system/accounts/LICENSE b/modules/utilities/windows/system/accounts/LICENSE
new file mode 100644
index 000000000..d64569567
--- /dev/null
+++ b/modules/utilities/windows/system/accounts/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/modules/utilities/windows/system/accounts/NOTICE b/modules/utilities/windows/system/accounts/NOTICE
new file mode 100644
index 000000000..d0eb3465c
--- /dev/null
+++ b/modules/utilities/windows/system/accounts/NOTICE
@@ -0,0 +1,17 @@
+accounts puppet module
+
+Copyright (C) 2012-2016 Puppet Labs, Inc.
+
+Puppet Labs can be contacted at: info@puppetlabs.com
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/modules/utilities/windows/system/accounts/README.markdown b/modules/utilities/windows/system/accounts/README.markdown
new file mode 100644
index 000000000..d9245d25b
--- /dev/null
+++ b/modules/utilities/windows/system/accounts/README.markdown
@@ -0,0 +1,194 @@
+# accounts
+
+#### Table of Contents
+1. [Description](#description)
+2. [Setup - The basics of getting started with accounts](#setup)
+3. [Usage - Configuration options and additional functionality](#usage)
+ * [Declare user accounts](#declare-user-accounts)
+ * [Customize the home directory](#customize-the-home-directory)
+ * [Lock accounts](#lock-accounts)
+ * [Manage SSH keys](#manage-ssh-keys)
+4. [Reference - An under-the-hood peek at what the module is doing and how](#reference)
+5. [Limitations - OS compatibility, etc.](#limitations)
+6. [Development - Guide for contributing to the module](#development)
+
+
+## Description
+
+The accounts module manages resources related to login and service accounts. This module replaces Puppet Enterprise's built-in pe\_accounts module, which is no longer included in PE 2015.3 and later versions.
+
+This module works on many UNIX/Linux operating systems. It does not support configuring accounts on Microsoft Windows platforms.
+
+## Setup
+
+### Beginning with accounts
+
+Declare the `accounts` class in a Puppet-managed node's manifest:
+
+~~~puppet
+node default {
+ accounts::user { 'dan': }
+ accounts::user { 'morgan': }
+}
+~~~
+
+The above example creates accounts, home directories, and groups for Dan and Morgan.
+
+## Usage
+
+### Declare user accounts
+
+~~~puppet
+accounts::user { 'bob':
+ uid => 4001,
+ gid => 4001,
+ shell => '/bin/bash',
+ password => '!!',
+ sshkeys => "ssh-rsa AAAA...",
+ locked => false,
+}
+~~~
+
+### Customize the home directory
+
+A simple bashrc and bash\_profile rc file is managed by Puppet for each account. These rc files add some simple aliases, update the prompt, add ~/bin to the path, and source the following files (which are not managed by this module) in the following order:
+
+ 1. `/etc/bashrc`
+ 2. `/etc/bashrc.puppet`
+ 3. `~/.bashrc.custom`
+
+Account holders can customize their shells by managing their bashrc.custom files. In addition, the system administrator can make profile changes that affect all accounts with a bash shell by managing the '/etc/bashrc.puppet' file.
+
+### Lock accounts
+
+Lock accounts by setting the `locked` parameter of an account to true.
+
+For example:
+
+~~~puppet
+accounts::user { 'villain':
+ comment => 'Bad Person',
+ locked => true
+}
+~~~
+
+The accounts module sets the account to an invalid shell appropriate for the system Puppet is managing and displays the following message if a user tries to access the account:
+
+~~~
+$ ssh villain@centos56
+This account is currently not available.
+Connection to 172.16.214.129 closed.
+~~~
+
+### Manage SSH keys
+
+Manage SSH keys with the `sshkeys` attribute of the `accounts::user` defined type. This parameter accepts an array of public key contents as strings.
+
+Example:
+
+~~~puppet
+accounts::user { 'jeff':
+ comment => 'Jeff McCune',
+ groups => [
+ 'admin',
+ 'sudonopw',
+ ],
+ uid => '1112',
+ gid => '1112',
+ sshkeys => [
+ 'ssh-rsa AAAAB3Nza...== jeff@puppetlabs.com',
+ 'ssh-dss AAAAB3Nza...== jeff@metamachine.net',
+ ],
+}
+~~~
+
+## Reference
+
+### Defined type: `accounts::user`
+
+This resource manages the user, group, .vim/, .ssh/, .bash\_profile, .bashrc, homedir, .ssh/authorized\_keys files, and directories.
+
+#### `bashrc_content`
+
+The content to place in the user's ~/.bashrc file. Default: undef.
+
+#### `bash_profile_content`
+
+The content to place in the user's ~/.bash\_profile file. Default: undef.
+
+#### `comment`
+
+A comment describing or regarding the user. Accepts a string. Default: '$name'.
+
+#### `ensure`
+
+Specifies whether the user, its primary group, homedir, and ssh keys should exist. Valid values are 'present' and 'absent'. Note that when a user is created, a group with the same name as the user is also created. Default: 'present'.
+
+#### `gid`
+
+Specifies the gid of the user's primary group. Must be specified numerically. Default: undef.
+
+#### `groups`
+
+Specifies the user's group memberships. Valid values: an array. Default: an empty array.
+
+#### `home`
+
+Specifies the path to the user's home directory.
+Default:
+* Linux, non-root user: '/home/$name'
+* Linux, root user: '/root'
+* Solaris, non-root user: '/export/home/$name'
+* Solaris, root user: '/'
+
+#### `home_mode`
+
+Manages the user's home directory permission mode. Valid values are in [octal notation](https://docs.puppetlabs.com/references/latest/type.html#file-attribute-mode), specified as a string. Defaults to `0700`, which gives the owner full read, write, and execute permissions, while group and other have no permissions.
+
+#### `locked`
+
+Specifies whether the account should be locked and the user prevented from logging in. Set to true for users whose login privileges have been revoked. Valid values: true, false. Default: false.
+
+#### `managehome`
+
+Specifies whether the user's home directory should be managed by puppet. In addition to the usual [user resource managehome](https://docs.puppetlabs.com/references/latest/type.html#user-attribute-managehome) qualities, this attribute also purges the user's homedir if `ensure` is set to 'absent' and `managehome` is set to true. Default: true.
+
+#### `membership`
+
+Establishes whether specified groups should be considered the complete list (inclusive) or the minimum list (minimum) of groups to which the user belongs. Valid values: 'inclusive', 'minimum'. Default: 'minimum'.
+
+#### `password`
+
+The user's password, in whatever encrypted format the local machine requires. Default: '!!', which prevents the user from logging in with a password.
+
+#### `purge_sshkeys`
+
+Whether keys not included in `sshkeys` should be removed from the user. If `purge_sshkeys` is true and `sshkeys` is an empty array, all SSH keys will be removed from the user. Valid values: true, false. Default: false.
+
+#### `shell`
+
+Manages the user shell. Default: '/bin/bash'.
+
+#### `sshkeys`
+
+An array of SSH public keys associated with the user. These should be complete public key strings that include the type and name of the key, exactly as the key would appear in its id\_rsa.pub or id\_dsa.pub file. Must be an array. Default: an empty array.
+
+#### `uid`
+
+Specifies the user's uid number. Must be specified numerically. Default: undef.
+
+## Limitations
+
+This module works with Puppet Enterprise 2015.3 and later.
+
+### Changes from pe\_accounts
+
+The accounts module is designed to take the place of the pe\_accounts module that shipped with PE versions 2015.2 and earlier. Some of the changes include the removal of the base class, improving the validation, and allowing more flexibility regarding which files should or should not be managed in a user's home directory.
+
+For example, the .bashrc and .bash\_profile files are not managed by default but allow custom content to be passed in using the `bashrc_content` and `bash_profile_content` parameters. The content for these two files as managed by pe\_accounts can continue to be used by passing `bashrc_content => file('accounts/shell/bashrc')` and `bash_profile_content => file('accounts/shell/bash_profile')` to the `accounts::user` defined type.
+
+## Development
+
+If you run into an issue with this module, or if you would like to request a feature, please [file a ticket](https://tickets.puppetlabs.com/browse/MODULES/).
+
+If you have problems getting this module up and running, please [contact Support](http://puppetlabs.com/services/customer-support).
diff --git a/modules/utilities/windows/system/accounts/Rakefile b/modules/utilities/windows/system/accounts/Rakefile
new file mode 100644
index 000000000..3e8d4cb95
--- /dev/null
+++ b/modules/utilities/windows/system/accounts/Rakefile
@@ -0,0 +1,38 @@
+require 'puppet_blacksmith/rake_tasks'
+require 'puppet-lint/tasks/puppet-lint'
+require 'puppetlabs_spec_helper/rake_tasks'
+
+PuppetLint.configuration.send('relative')
+PuppetLint.configuration.send('disable_documentation')
+PuppetLint.configuration.send('disable_single_quote_string_with_variables')
+
+desc 'Generate pooler nodesets'
+task :gen_nodeset do
+ require 'beaker-hostgenerator'
+ require 'securerandom'
+ require 'fileutils'
+
+ agent_target = ENV['TEST_TARGET']
+ if ! agent_target
+ STDERR.puts 'TEST_TARGET environment variable is not set'
+ STDERR.puts 'setting to default value of "redhat-64default."'
+ agent_target = 'redhat-64default.'
+ end
+
+ master_target = ENV['MASTER_TEST_TARGET']
+ if ! master_target
+ STDERR.puts 'MASTER_TEST_TARGET environment variable is not set'
+ STDERR.puts 'setting to default value of "redhat7-64mdcl"'
+ master_target = 'redhat7-64mdcl'
+ end
+
+ targets = "#{master_target}-#{agent_target}"
+ cli = BeakerHostGenerator::CLI.new([targets])
+ nodeset_dir = "tmp/nodesets"
+ nodeset = "#{nodeset_dir}/#{targets}-#{SecureRandom.uuid}.yaml"
+ FileUtils.mkdir_p(nodeset_dir)
+ File.open(nodeset, 'w') do |fh|
+ fh.print(cli.execute)
+ end
+ puts nodeset
+end
diff --git a/modules/utilities/windows/system/accounts/accounts.pp b/modules/utilities/windows/system/accounts/accounts.pp
new file mode 100644
index 000000000..178c52748
--- /dev/null
+++ b/modules/utilities/windows/system/accounts/accounts.pp
@@ -0,0 +1,11 @@
+# accounts::user { 'user':
+# uid => '4001',
+# gid => '4001',
+# shell => '/bin/bash',
+# name => 'user',
+# # password = password
+# password => '$6$$bLTg4cpho8PIUrjfsE7qlU08Qx2UEfw..xOc6I1wpGVtyVYToGrr7BzRdAAnEr5lYFr1Z9WcCf1xNZ1HG9qFW1',
+#
+# # sshkeys => "ssh-rsa AAAA...",
+# # locked => false,
+# }
\ No newline at end of file
diff --git a/modules/utilities/windows/system/accounts/checksums.json b/modules/utilities/windows/system/accounts/checksums.json
new file mode 100644
index 000000000..520e9587e
--- /dev/null
+++ b/modules/utilities/windows/system/accounts/checksums.json
@@ -0,0 +1,27 @@
+{
+ "CHANGELOG.md": "7acf5457a1d2aecbc66de472e405886e",
+ "CONTRIBUTING.md": "b78f71c1104f00538d50ad2775f58e95",
+ "Gemfile": "d197734139f44a800a88d49ad4c34192",
+ "LICENSE": "3b83ef96387f14655fc854ddc3c6bd57",
+ "NOTICE": "feab5860b912f2448cec4e01208a4c13",
+ "README.markdown": "66475b3069dc829325f59e53d7d0c4b9",
+ "Rakefile": "6db744f1deed7ae0746abe59e3d50863",
+ "examples/user_group.pp": "afcbd38c69bdb4baf06590ece91c0b7d",
+ "examples/user_group_hash.pp": "9cb2af2e0e1f4b4822b196f86c60ce3a",
+ "files/shell/bash_profile": "546be8ec0dfc6a62ceb331a4430f30f5",
+ "files/shell/bashrc": "776e5327b6b492e2b49f84a41f6f68dd",
+ "manifests/home_dir.pp": "463a878a924f759a8607bc92a7c8ce89",
+ "manifests/manage_keys.pp": "ece14075dde2b03f7d8f4db350311f8e",
+ "manifests/user.pp": "97fb4ddfd72e27c552cb4b3a3d326d4a",
+ "metadata.json": "a2633bb611dab42d7c829e61f0fde66b",
+ "spec/acceptance/nodesets/centos-7-x64.yml": "a713f3abd3657f0ae2878829badd23cd",
+ "spec/acceptance/nodesets/debian-8-x64.yml": "d2d2977900989f30086ad251a14a1f39",
+ "spec/acceptance/nodesets/default.yml": "b42da5a1ea0c964567ba7495574b8808",
+ "spec/acceptance/nodesets/docker/centos-7.yml": "8a3892807bdd62306ae4774f41ba11ae",
+ "spec/acceptance/nodesets/docker/debian-8.yml": "ac8e871d1068c96de5e85a89daaec6df",
+ "spec/acceptance/nodesets/docker/ubuntu-14.04.yml": "dc42ee922a96908d85b8f0f08203ce58",
+ "spec/acceptance/user_spec.rb": "4d58fcae36ef9e7998abaf2f27ef1e56",
+ "spec/defines/accounts_user_spec.rb": "ff0219f64d3b653d415f4812d1d073c7",
+ "spec/spec_helper.rb": "b2db3bc02b4ac2fd5142a6621c641b07",
+ "spec/spec_helper_acceptance.rb": "32ef084d528663244770e7b225c84e93"
+}
\ No newline at end of file
diff --git a/modules/utilities/windows/system/accounts/examples/user_group.pp b/modules/utilities/windows/system/accounts/examples/user_group.pp
new file mode 100644
index 000000000..4ab618bd4
--- /dev/null
+++ b/modules/utilities/windows/system/accounts/examples/user_group.pp
@@ -0,0 +1,37 @@
+accounts::group { 'admin':
+ gid => 3000,
+}
+accounts::group { 'sudo':
+ gid => 3001,
+}
+accounts::group { 'sudonopw':
+ gid => 3002,
+}
+accounts::group { 'developer':
+ gid => 3003,
+}
+accounts::group { 'ops':
+ gid => 3004,
+}
+
+accounts::user { 'jeff':
+ shell => '/bin/zsh',
+ comment => 'Jeff McCune',
+ groups => [
+ 'admin',
+ 'sudonopw',
+ ],
+ uid => 1112,
+ gid => 1112,
+ locked => true,
+ sshkeys => [
+ 'ssh-rsa AAAA...',
+ 'ssh-dss AAAA...',
+ ],
+ password => '!!',
+}
+accounts::user { 'dan':
+ comment => 'Dan Bode',
+ uid => '1109',
+ gid => '1109',
+}
diff --git a/modules/utilities/windows/system/accounts/examples/user_group_hash.pp b/modules/utilities/windows/system/accounts/examples/user_group_hash.pp
new file mode 100644
index 000000000..79956cd85
--- /dev/null
+++ b/modules/utilities/windows/system/accounts/examples/user_group_hash.pp
@@ -0,0 +1,31 @@
+# Use a variable OR with hiera_hash():
+$groups_hash = {
+ 'admin' => { gid => '3000' },
+ 'sudo' => { gid => '3001' },
+ 'sudonopw' => { gid => '3002' },
+ 'developer' => { gid => '3003' },
+ 'ops' => { gid => '3004' },
+}
+create_resources('accounts::group', $groups_hash)
+
+$users_hash = {
+ 'jeff' => {
+ 'shell' => '/bin/zsh',
+ 'comment' => 'Jeff McCune',
+ 'groups' => [ admin, sudonopw, ],
+ 'uid' => '1112',
+ 'gid' => '1112',
+ 'locked' => true,
+ 'sshkeys' => [
+ 'ssh-rsa AAAA...',
+ 'ssh-dss AAAA...',
+ ],
+ 'password' => '!!',
+ },
+ 'dan' => {
+ 'comment' => 'Dan Bode',
+ 'uid' => '1109',
+ 'gid' => '1109',
+ },
+}
+create_resources('accounts::user', $users_hash)
diff --git a/modules/utilities/windows/system/accounts/files/shell/bash_profile b/modules/utilities/windows/system/accounts/files/shell/bash_profile
new file mode 100644
index 000000000..026233f08
--- /dev/null
+++ b/modules/utilities/windows/system/accounts/files/shell/bash_profile
@@ -0,0 +1,13 @@
+# .bash_profile
+
+# Get the aliases and functions
+if [ -f ~/.bashrc ]; then
+ . ~/.bashrc
+fi
+
+# User specific environment and startup programs
+
+PATH=$PATH:$HOME/bin
+
+export PATH
+unset USERNAME
diff --git a/modules/utilities/windows/system/accounts/files/shell/bashrc b/modules/utilities/windows/system/accounts/files/shell/bashrc
new file mode 100644
index 000000000..e866f7a1e
--- /dev/null
+++ b/modules/utilities/windows/system/accounts/files/shell/bashrc
@@ -0,0 +1,27 @@
+# .bashrc
+# This file is managed by Puppet. Changes will be overwritten.
+# Please edit ~/.bashrc.custom instead of this file!
+
+# User specific aliases and functions
+
+alias rm='rm -i'
+alias cp='cp -i'
+alias mv='mv -i'
+
+# Set the prompt
+PS1='\u@\h:\w\$ '
+
+# Source global definitions
+if [ -f /etc/bashrc ]; then
+ . /etc/bashrc
+fi
+
+# Puppet specific definitions
+if [ -f /etc/bashrc.puppet ]; then
+ . /etc/bashrc.puppet
+fi
+
+# Account holder definitions
+if [ -f ~/.bashrc.custom ]; then
+ . ~/.bashrc.custom
+fi
diff --git a/modules/utilities/windows/system/accounts/manifests/home_dir.pp b/modules/utilities/windows/system/accounts/manifests/home_dir.pp
new file mode 100644
index 000000000..cc115d01a
--- /dev/null
+++ b/modules/utilities/windows/system/accounts/manifests/home_dir.pp
@@ -0,0 +1,92 @@
+#
+# Specified how home directories are managed.
+#
+# [*name*] Name of the home directory that is being managed.
+# [*user*] User that owns all of the files being created.
+# [*sshkeys*] List of ssh keys to be added for this user in this
+# directory
+define accounts::home_dir(
+ $user,
+ $bashrc_content = undef,
+ $bash_profile_content = undef,
+ $mode = '0700',
+ $ensure = 'present',
+ $managehome = true,
+ $sshkeys = [],
+) {
+ validate_re($ensure, '^(present|absent)$')
+
+ if $ensure == 'absent' and $managehome == true {
+ file { $name:
+ ensure => absent,
+ recurse => true,
+ force => true,
+ }
+ } elsif $ensure == 'present' and $managehome == true {
+
+ $key_file = "${name}/.ssh/authorized_keys"
+
+ # Solaris homedirs are managed in zfs by `useradd -m`. If the directory
+ # does not yet exist then we can't predict how it should be created, but we
+ # should still manage the user/group/mode
+ file { $name:
+ ensure => directory,
+ owner => $user,
+ group => $user,
+ mode => $mode,
+ }
+
+ file { "${name}/.ssh":
+ ensure => directory,
+ owner => $user,
+ group => $user,
+ mode => '0700',
+ }
+
+ file { "${name}/.vim":
+ ensure => directory,
+ owner => $user,
+ group => $user,
+ mode => '0700',
+ }
+
+ if $bashrc_content {
+ file { "${name}/.bashrc":
+ ensure => file,
+ content => $bashrc_content,
+ owner => $user,
+ group => $user,
+ mode => '0644',
+ }
+ }
+ if $bash_profile_content {
+ file { "${name}/.bash_profile":
+ ensure => file,
+ content => $bash_profile_content,
+ owner => $user,
+ group => $user,
+ mode => '0644',
+ }
+ }
+
+ file { $key_file:
+ ensure => file,
+ owner => $user,
+ group => $user,
+ mode => '0600',
+ }
+
+ if $sshkeys != [] {
+ accounts::manage_keys { $sshkeys:
+ user => $user,
+ key_file => $key_file,
+ require => File["${name}/.ssh"],
+ before => File[$key_file],
+ }
+ }
+ } elsif $managehome == false {
+ if $sshkeys != [] {
+ warning("ssh keys were passed for user ${user} but \$managehome is set to false; not managing user ssh keys")
+ }
+ }
+}
diff --git a/modules/utilities/windows/system/accounts/manifests/manage_keys.pp b/modules/utilities/windows/system/accounts/manifests/manage_keys.pp
new file mode 100644
index 000000000..a5bbf32c0
--- /dev/null
+++ b/modules/utilities/windows/system/accounts/manifests/manage_keys.pp
@@ -0,0 +1,21 @@
+#
+define accounts::manage_keys(
+ $user,
+ $key_file,
+) {
+
+ $key_array = split($name, ' ')
+ $key_type = $key_array[0]
+ $key_content = $key_array[1]
+ $key_name = $key_array[2]
+ $key_title = "${user}_${key_type}_${key_name}"
+
+ ssh_authorized_key { $key_title:
+ ensure => present,
+ user => $user,
+ name => $key_name,
+ key => $key_content,
+ type => $key_type,
+ target => $key_file,
+ }
+}
diff --git a/modules/utilities/windows/system/accounts/manifests/user.pp b/modules/utilities/windows/system/accounts/manifests/user.pp
new file mode 100644
index 000000000..b57b74a76
--- /dev/null
+++ b/modules/utilities/windows/system/accounts/manifests/user.pp
@@ -0,0 +1,123 @@
+#
+#
+# parameters:
+# [*name*] Name of user
+# [*locked*] Whether the user account should be locked.
+# [*sshkeys*] List of ssh public keys to be associated with the
+# user.
+# [*managehome*] Whether the home directory should be removed with accounts
+#
+define accounts::user(
+ $ensure = 'present',
+ $shell = '/bin/bash',
+ $comment = $name,
+ $home = undef,
+ $home_mode = undef,
+ $uid = undef,
+ $gid = undef,
+ $groups = [ ],
+ $membership = 'minimum',
+ $password = '!!',
+ $locked = false,
+ $sshkeys = [],
+ $purge_sshkeys = false,
+ $managehome = true,
+ $bashrc_content = undef,
+ $bash_profile_content = undef,
+) {
+ validate_re($ensure, '^present$|^absent$')
+ validate_bool($locked, $managehome, $purge_sshkeys)
+ validate_re($shell, '^/')
+ validate_string($comment, $password)
+ validate_array($groups, $sshkeys)
+ validate_re($membership, '^inclusive$|^minimum$')
+ if $bashrc_content {
+ validate_string($bashrc_content)
+ }
+ if $bash_profile_content {
+ validate_string($bash_profile_content)
+ }
+ if $home {
+ validate_re($home, '^/')
+ # If the home directory is not / (root on solaris) then disallow trailing slashes.
+ validate_re($home, '^/$|[^/]$')
+ }
+
+ if $home {
+ $home_real = $home
+ } elsif $name == 'root' {
+ $home_real = $::osfamily ? {
+ 'Solaris' => '/',
+ default => '/root',
+ }
+ } else {
+ $home_real = $::osfamily ? {
+ 'Solaris' => "/export/home/${name}",
+ default => "/home/${name}",
+ }
+ }
+
+ if $uid != undef {
+ validate_re($uid, '^\d+$')
+ }
+
+ if $gid != undef {
+ validate_re($gid, '^\d+$')
+ $_gid = $gid
+ } else {
+ $_gid = $name
+ }
+
+ if $locked {
+ case $::operatingsystem {
+ 'debian', 'ubuntu' : {
+ $_shell = '/usr/sbin/nologin'
+ }
+ 'solaris' : {
+ $_shell = '/usr/bin/false'
+ }
+ default : {
+ $_shell = '/sbin/nologin'
+ }
+ }
+ } else {
+ $_shell = $shell
+ }
+
+ user { $name:
+ ensure => $ensure,
+ shell => $_shell,
+ comment => "${comment}", # lint:ignore:only_variable_string
+ home => $home_real,
+ uid => $uid,
+ gid => $_gid,
+ groups => $groups,
+ membership => $membership,
+ managehome => $managehome,
+ password => $password,
+ purge_ssh_keys => $purge_sshkeys,
+ }
+
+ # use $gid instead of $_gid since `gid` in group can only take a number
+ group { $name:
+ ensure => $ensure,
+ gid => $gid,
+ }
+
+ if $ensure == 'present' {
+ Group[$name] -> User[$name]
+ } else {
+ User[$name] -> Group[$name]
+ }
+
+ accounts::home_dir { $home_real:
+ ensure => $ensure,
+ mode => $home_mode,
+ managehome => $managehome,
+ bashrc_content => $bashrc_content,
+ bash_profile_content => $bash_profile_content,
+ user => $name,
+ sshkeys => $sshkeys,
+ require => [ User[$name], Group[$name] ],
+ }
+}
diff --git a/modules/utilities/windows/system/accounts/metadata.json b/modules/utilities/windows/system/accounts/metadata.json
new file mode 100644
index 000000000..147c84213
--- /dev/null
+++ b/modules/utilities/windows/system/accounts/metadata.json
@@ -0,0 +1,78 @@
+{
+ "name": "puppetlabs-accounts",
+ "version": "1.1.0",
+ "author": "puppetlabs",
+ "summary": "Account management module.",
+ "license": "Apache-2.0",
+ "source": "git://github.com/puppetlabs/puppetlabs-accounts.git",
+ "project_page": "https://github.com/puppetlabs/puppetlabs-accounts",
+ "issues_url": "https://tickets.puppetlabs.com/browse/MODULES",
+ "dependencies": [
+ {"name":"puppetlabs/stdlib","version_requirement":">= 3.4.0 < 5.0.0"}
+ ],
+ "data_provider": null,
+ "operatingsystem_support": [
+ {
+ "operatingsystem": "RedHat",
+ "operatingsystemrelease": [
+ "6",
+ "7"
+ ]
+ },
+ {
+ "operatingsystem": "CentOS",
+ "operatingsystemrelease": [
+ "6",
+ "7"
+ ]
+ },
+ {
+ "operatingsystem": "OracleLinux",
+ "operatingsystemrelease": [
+ "6",
+ "7"
+ ]
+ },
+ {
+ "operatingsystem": "Scientific",
+ "operatingsystemrelease": [
+ "6",
+ "7"
+ ]
+ },
+ {
+ "operatingsystem": "Debian",
+ "operatingsystemrelease": [
+ "7",
+ "8"
+ ]
+ },
+ {
+ "operatingsystem": "SLES",
+ "operatingsystemrelease": [
+ "11 SP1"
+ ]
+ },
+ {
+ "operatingsystem": "Ubuntu",
+ "operatingsystemrelease": [
+ "12.04",
+ "14.04",
+ "16.04"
+ ]
+ },
+ {
+ "operatingsystem": "Solaris",
+ "operatingsystemrelease": [
+ "11"
+ ]
+ }
+ ],
+ "requirements": [
+ {
+ "name": "puppet",
+ "version_requirement": ">= 4.0.0 < 5.0.0"
+ }
+ ],
+ "description": "Account management module."
+}
diff --git a/modules/utilities/windows/system/accounts/secgen_metadata.xml b/modules/utilities/windows/system/accounts/secgen_metadata.xml
new file mode 100644
index 000000000..d35fc4fb3
--- /dev/null
+++ b/modules/utilities/windows/system/accounts/secgen_metadata.xml
@@ -0,0 +1,18 @@
+
+
+
+ User account service
+ Puppet Labs
+ Jason Keighley
+ Apache v2
+ User account creation and modification
+
+ system
+ linux
+
+
+ https://forge.puppet.com/puppetlabs/accounts
+
+
\ No newline at end of file
diff --git a/modules/utilities/windows/system/accounts/spec/acceptance/nodesets/centos-7-x64.yml b/modules/utilities/windows/system/accounts/spec/acceptance/nodesets/centos-7-x64.yml
new file mode 100644
index 000000000..5eebdefbf
--- /dev/null
+++ b/modules/utilities/windows/system/accounts/spec/acceptance/nodesets/centos-7-x64.yml
@@ -0,0 +1,10 @@
+HOSTS:
+ centos-7-x64:
+ roles:
+ - agent
+ - default
+ platform: el-7-x86_64
+ hypervisor: vagrant
+ box: puppetlabs/centos-7.2-64-nocm
+CONFIG:
+ type: foss
diff --git a/modules/utilities/windows/system/accounts/spec/acceptance/nodesets/debian-8-x64.yml b/modules/utilities/windows/system/accounts/spec/acceptance/nodesets/debian-8-x64.yml
new file mode 100644
index 000000000..fef6e63ca
--- /dev/null
+++ b/modules/utilities/windows/system/accounts/spec/acceptance/nodesets/debian-8-x64.yml
@@ -0,0 +1,10 @@
+HOSTS:
+ debian-8-x64:
+ roles:
+ - agent
+ - default
+ platform: debian-8-amd64
+ hypervisor: vagrant
+ box: puppetlabs/debian-8.2-64-nocm
+CONFIG:
+ type: foss
diff --git a/modules/utilities/windows/system/accounts/spec/acceptance/nodesets/default.yml b/modules/utilities/windows/system/accounts/spec/acceptance/nodesets/default.yml
new file mode 100644
index 000000000..dba339c46
--- /dev/null
+++ b/modules/utilities/windows/system/accounts/spec/acceptance/nodesets/default.yml
@@ -0,0 +1,10 @@
+HOSTS:
+ ubuntu-1404-x64:
+ roles:
+ - agent
+ - default
+ platform: ubuntu-14.04-amd64
+ hypervisor: vagrant
+ box: puppetlabs/ubuntu-14.04-64-nocm
+CONFIG:
+ type: foss
diff --git a/modules/utilities/windows/system/accounts/spec/acceptance/nodesets/docker/centos-7.yml b/modules/utilities/windows/system/accounts/spec/acceptance/nodesets/docker/centos-7.yml
new file mode 100644
index 000000000..a3333aac5
--- /dev/null
+++ b/modules/utilities/windows/system/accounts/spec/acceptance/nodesets/docker/centos-7.yml
@@ -0,0 +1,12 @@
+HOSTS:
+ centos-7-x64:
+ platform: el-7-x86_64
+ hypervisor: docker
+ image: centos:7
+ docker_preserve_image: true
+ docker_cmd: '["/usr/sbin/init"]'
+ # install various tools required to get the image up to usable levels
+ docker_image_commands:
+ - 'yum install -y crontabs tar wget openssl sysvinit-tools iproute which initscripts'
+CONFIG:
+ trace_limit: 200
diff --git a/modules/utilities/windows/system/accounts/spec/acceptance/nodesets/docker/debian-8.yml b/modules/utilities/windows/system/accounts/spec/acceptance/nodesets/docker/debian-8.yml
new file mode 100644
index 000000000..df5c31944
--- /dev/null
+++ b/modules/utilities/windows/system/accounts/spec/acceptance/nodesets/docker/debian-8.yml
@@ -0,0 +1,11 @@
+HOSTS:
+ debian-8-x64:
+ platform: debian-8-amd64
+ hypervisor: docker
+ image: debian:8
+ docker_preserve_image: true
+ docker_cmd: '["/sbin/init"]'
+ docker_image_commands:
+ - 'apt-get update && apt-get install -y net-tools wget locales strace lsof && echo "en_US.UTF-8 UTF-8" > /etc/locale.gen && locale-gen'
+CONFIG:
+ trace_limit: 200
diff --git a/modules/utilities/windows/system/accounts/spec/acceptance/nodesets/docker/ubuntu-14.04.yml b/modules/utilities/windows/system/accounts/spec/acceptance/nodesets/docker/ubuntu-14.04.yml
new file mode 100644
index 000000000..b1efa5839
--- /dev/null
+++ b/modules/utilities/windows/system/accounts/spec/acceptance/nodesets/docker/ubuntu-14.04.yml
@@ -0,0 +1,12 @@
+HOSTS:
+ ubuntu-1404-x64:
+ platform: ubuntu-14.04-amd64
+ hypervisor: docker
+ image: ubuntu:14.04
+ docker_preserve_image: true
+ docker_cmd: '["/sbin/init"]'
+ docker_image_commands:
+ # ensure that upstart is booting correctly in the container
+ - 'rm /usr/sbin/policy-rc.d && rm /sbin/initctl && dpkg-divert --rename --remove /sbin/initctl && apt-get update && apt-get install -y net-tools wget && locale-gen en_US.UTF-8'
+CONFIG:
+ trace_limit: 200
diff --git a/modules/utilities/windows/system/accounts/spec/acceptance/user_spec.rb b/modules/utilities/windows/system/accounts/spec/acceptance/user_spec.rb
new file mode 100644
index 000000000..734422e35
--- /dev/null
+++ b/modules/utilities/windows/system/accounts/spec/acceptance/user_spec.rb
@@ -0,0 +1,87 @@
+require 'spec_helper_acceptance'
+
+describe 'accounts::user define', :unless => UNSUPPORTED_PLATFORMS.include?(fact("osfamily")) do
+ describe 'main tests' do
+ describe user('hunner') do
+ it 'creates groups of matching names, assigns non-matching group, manages homedir, manages other properties, gives key, makes dotfiles' do
+ pp = <<-EOS
+ file { '/test':
+ ensure => directory,
+ before => Accounts::User['hunner'],
+ }
+ accounts::user { 'hunner':
+ groups => ['root'],
+ password => 'hi',
+ shell => '/bin/true',
+ home => '/test/hunner',
+ bashrc_content => file('accounts/shell/bashrc'),
+ bash_profile_content => file('accounts/shell/bash_profile'),
+ sshkeys => [
+ 'ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant',
+ ],
+ }
+ EOS
+ apply_manifest(pp, :catch_failures => true)
+ end
+ it { should exist }
+ it { should belong_to_group 'hunner' }
+ it { should belong_to_group 'root' }
+ it { should have_login_shell '/bin/true' }
+ it { should have_home_directory '/test/hunner' }
+ # Solaris 10's /bin/sh can't expand ~username paths and thus can't read ~hunner/.ssh/authorized_keys
+ it("should have authorized_key", :unless => default['platform'].match(/solaris-10/)) { should have_authorized_key 'ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant' }
+ end
+ describe file('/test/hunner') do
+ it { should be_directory }
+ it { should be_mode 700 }
+ it { should be_owned_by 'hunner' }
+ it { should be_grouped_into 'hunner' }
+ end
+ describe file('/test/hunner/.bashrc') do
+ its(:content) { should match(/managed by Puppet/) }
+ end
+ describe file('/test/hunner/.bash_profile') do
+ its(:content) { should match(/Get the aliases and functions/) }
+ end
+ describe file('/test/hunner/.vim') do
+ it { should be_directory }
+ end
+ end
+ describe 'warn for sshkeys without managehome' do
+ it 'creates groups of matching names, assigns non-matching group, manages homedir, manages other properties, gives key, makes dotfiles' do
+ pp = <<-EOS
+ accounts::user { 'hunner':
+ managehome => false,
+ sshkeys => [
+ 'ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant',
+ ],
+ }
+ EOS
+ apply_manifest(pp, :catch_failures => true) do |r|
+ expect(r.stderr).to match(/Warning:.*ssh keys were passed for user hunner/)
+ end
+ end
+ end
+ describe 'locking users' do
+ describe user('hunner') do
+ it 'locks a user' do
+ pp = <<-EOS
+ accounts::user { 'hunner':
+ locked => true,
+ }
+ EOS
+ apply_manifest(pp, :catch_failures => true)
+ end
+ it {
+ case fact('osfamily')
+ when 'Debian'
+ should have_login_shell '/usr/sbin/nologin'
+ when 'Solaris'
+ should have_login_shell '/usr/bin/false'
+ else
+ should have_login_shell '/sbin/nologin'
+ end
+ }
+ end
+ end
+end
diff --git a/modules/utilities/windows/system/accounts/spec/defines/accounts_user_spec.rb b/modules/utilities/windows/system/accounts/spec/defines/accounts_user_spec.rb
new file mode 100644
index 000000000..48a9a74c3
--- /dev/null
+++ b/modules/utilities/windows/system/accounts/spec/defines/accounts_user_spec.rb
@@ -0,0 +1,185 @@
+require 'spec_helper'
+
+describe '::accounts::user' do
+ let(:title) { "dan" }
+ let(:params) { {} }
+ let(:facts) { {} }
+
+ describe 'expected defaults' do
+ let(:facts) { { :osfamily => "Debian" } }
+ it { is_expected.to contain_user('dan').with({'shell' => '/bin/bash'}) }
+ it { is_expected.to contain_user('dan').with({'home' => "/home/#{title}"}) }
+ it { is_expected.to contain_user('dan').with({'ensure' => 'present'}) }
+ it { is_expected.to contain_user('dan').with({'comment' => title}) }
+ it { is_expected.to contain_user('dan').with({'groups' => []}) }
+ it { is_expected.to contain_user('dan').with({'managehome' => true }) }
+ it { is_expected.to contain_group('dan').with({'ensure' => 'present'}) }
+ it { is_expected.to contain_group('dan').with({'gid' => nil}) }
+ end
+
+ describe 'expected home defaults' do
+ context 'normal user on linux' do
+ let(:title) { "dan" }
+ let(:facts) { { :osfamily => "Debian" } }
+ it { is_expected.to contain_user('dan').with_home('/home/dan') }
+ end
+ context 'root user on linux' do
+ let(:title) { "root" }
+ let(:facts) { { :osfamily => "Debian" } }
+ it { is_expected.to contain_user('root').with_home('/root') }
+ end
+ context 'normal user on Solaris' do
+ let(:title) { "dan" }
+ let(:facts) { { :osfamily => "Solaris" } }
+ it { is_expected.to contain_user('dan').with_home('/export/home/dan') }
+ end
+ context 'root user on Solaris' do
+ let(:title) { "root" }
+ let(:facts) { { :osfamily => "Solaris" } }
+ it { is_expected.to contain_user('root').with_home('/') }
+ end
+ end
+
+ describe 'when setting user parameters' do
+ before do
+ params['ensure'] = 'present'
+ params['shell'] = '/bin/csh'
+ params['comment'] = 'comment'
+ params['home'] = '/var/home/dan'
+ params['home_mode'] = '0755'
+ params['uid'] = '123'
+ params['gid'] = '456'
+ params['groups'] = ['admin']
+ params['membership'] = 'inclusive'
+ params['password'] = 'foo'
+ params['sshkeys'] = ['1 2 3', '2 3 4']
+ end
+
+ it { is_expected.to contain_user('dan').with({'ensure' => 'present'}) }
+ it { is_expected.to contain_user('dan').with({'shell' => '/bin/csh'}) }
+ it { is_expected.to contain_user('dan').with({'comment' => 'comment'}) }
+ it { is_expected.to contain_user('dan').with({'home' => '/var/home/dan'}) }
+ it { is_expected.to contain_user('dan').with({'uid' => '123'}) }
+ it { is_expected.to contain_user('dan').with({'gid' => '456'}) }
+ it { is_expected.to contain_user('dan').with({'groups' => ['admin']}) }
+ it { is_expected.to contain_user('dan').with({'membership' => 'inclusive'}) }
+ it { is_expected.to contain_user('dan').with({'password' => 'foo'}) }
+ it { is_expected.to contain_group('dan').with({'ensure' => 'present'}) }
+ it { is_expected.to contain_group('dan').with({'gid' => '456'}) }
+ it { is_expected.to contain_group('dan').that_comes_before('User[dan]') }
+ it { is_expected.to contain_accounts__home_dir('/var/home/dan').with({'user' => title}) }
+ it { is_expected.to contain_accounts__home_dir('/var/home/dan').with({'mode' => '0755'}) }
+ it { is_expected.to contain_accounts__home_dir('/var/home/dan').with({'sshkeys' => ['1 2 3', '2 3 4']}) }
+ it { is_expected.to contain_file('/var/home/dan/.ssh') }
+
+ describe 'when setting the user to absent' do
+
+ # when deleting users the home dir is a File resource instead of a accounts::home_dir
+ let(:contain_home_dir) { contain_file('/var/home/dan') }
+
+ before do
+ params['ensure'] = 'absent'
+ end
+
+ it { is_expected.to contain_user('dan').with({'ensure' => 'absent'}) }
+ it { is_expected.to contain_user('dan').that_comes_before('Group[dan]') }
+ it { is_expected.to contain_group('dan').with({'ensure' => 'absent'}) }
+ it do
+ is_expected.to_not contain_accounts__home_dir('/var/home/dan').with({
+ 'ensure' => 'absent',
+ 'recurse' => true,
+ 'force' => true
+ })
+ end
+
+ describe 'with managehome off' do
+
+ before do
+ params['managehome'] = false
+ end
+
+ it { is_expected.not_to contain_home_dir }
+ it { is_expected.not_to contain_file('/var/home/dan/.ssh') }
+ end
+ end
+ end
+
+ describe 'invalid parameter values' do
+ it 'should only accept absent and present for ensure' do
+ params['ensure'] = 'invalid'
+ expect { subject.call }.to raise_error Puppet::Error
+ end
+ it 'should fail if locked is not a boolean' do
+ params['locked'] = 'true'
+ expect { subject.call }.to raise_error Puppet::Error
+ end
+ ['home', 'shell'].each do |param|
+ it "should fail is #{param} does not start with '/'" do
+ params[param] = 'no_leading_slash'
+ expect { subject.call }.to raise_error Puppet::Error
+ end
+ end
+ it 'should fail if gid is not composed of digits' do
+ params['gid'] = 'name'
+ expect { subject.call }.to raise_error Puppet::Error
+ end
+ it 'should not accept non-boolean values for locked' do
+ params['locked'] = 'false'
+ expect { subject.call }.to raise_error Puppet::Error
+ end
+ it 'should not accept non-boolean values for managehome' do
+ params['managehome'] = 'false'
+ expect { subject.call }.to raise_error Puppet::Error
+ end
+ end
+
+ describe 'when locking users' do
+
+ let(:params) { { 'locked' => true } }
+
+ describe 'on debian' do
+ before { facts['operatingsystem'] = 'debian' }
+ before { facts['osfamily'] = 'Debian' }
+ it { is_expected.to contain_user('dan').with({'shell' => '/usr/sbin/nologin'}) }
+ end
+
+ describe 'on ubuntu' do
+ before { facts['operatingsystem'] = 'ubuntu' }
+ before { facts['osfamily'] = 'Ubuntu' }
+ it { is_expected.to contain_user('dan').with({'shell' => '/usr/sbin/nologin'}) }
+ end
+
+ describe 'on solaris' do
+ before { facts['operatingsystem'] = 'solaris' }
+ before { facts['osfamily'] = 'Solaris' }
+ it { is_expected.to contain_user('dan').with({'shell' => '/usr/bin/false'}) }
+ end
+
+ describe 'on all other platforms' do
+ before { facts['operatingsystem'] = 'anything_else' }
+ before { facts['osfamily'] = 'anything_else' }
+ it { is_expected.to contain_user('dan').with({'shell' => '/sbin/nologin'}) }
+ end
+ end
+
+ describe 'when supplying resource defaults' do
+ before do
+ facts['osfamily'] = 'Debian'
+ facts['operatingsystem'] = 'debian'
+ end
+
+ let(:pre_condition) { "Accounts::User{ shell => '/bin/zsh' }" }
+
+ it { is_expected.to contain_user('dan').with({'shell' => '/bin/zsh'}) }
+
+ describe 'override defaults' do
+ let(:params) { { 'shell' => '/bin/csh' } }
+ it { is_expected.to contain_user('dan').with({'shell' => '/bin/csh'}) }
+ end
+
+ describe 'locked overrides should override defaults and user params' do
+ let(:params) { { 'shell' => '/bin/csh', 'locked' => true} }
+ it { is_expected.to contain_user('dan').with({'shell' => '/usr/sbin/nologin'}) }
+ end
+ end
+end
diff --git a/modules/utilities/windows/system/accounts/spec/spec_helper.rb b/modules/utilities/windows/system/accounts/spec/spec_helper.rb
new file mode 100644
index 000000000..22d5d689f
--- /dev/null
+++ b/modules/utilities/windows/system/accounts/spec/spec_helper.rb
@@ -0,0 +1,8 @@
+#This file is generated by ModuleSync, do not edit.
+require 'puppetlabs_spec_helper/module_spec_helper'
+
+# put local configuration and setup into spec_helper_local
+begin
+ require 'spec_helper_local'
+rescue LoadError
+end
diff --git a/modules/utilities/windows/system/accounts/spec/spec_helper_acceptance.rb b/modules/utilities/windows/system/accounts/spec/spec_helper_acceptance.rb
new file mode 100644
index 000000000..de7c2fbe6
--- /dev/null
+++ b/modules/utilities/windows/system/accounts/spec/spec_helper_acceptance.rb
@@ -0,0 +1,17 @@
+require 'beaker-rspec'
+require 'beaker/puppet_install_helper'
+
+UNSUPPORTED_PLATFORMS = ['windows', 'Darwin']
+
+run_puppet_install_helper
+
+RSpec.configure do |c|
+ proj_root = File.expand_path(File.join(File.dirname(__FILE__), '..'))
+ c.formatter = :documentation
+ c.before :suite do
+ hosts.each do |host|
+ copy_module_to(host, :source => proj_root, :module_name => 'accounts')
+ on host, puppet('module','install','puppetlabs-stdlib'), { :acceptable_exit_codes => [0,1] }
+ end
+ end
+end
diff --git a/modules/utilities/windows/system/local_security_policy/CHANGELOG.md b/modules/utilities/windows/system/local_security_policy/CHANGELOG.md
new file mode 100644
index 000000000..4f78f02eb
--- /dev/null
+++ b/modules/utilities/windows/system/local_security_policy/CHANGELOG.md
@@ -0,0 +1,29 @@
+###[0.6.3] - naeem98
+ * Added 'Accounts: Administrator account status' setting for CIS 2.3.1.1
+
+###[0.6.2]
+ * Bug fix for 'No auditing' case issue - Jordan Wesolowski - #26
+ * Fix issue where WMIC was timing out or crashing on systems joined to a domain - Thomas Linkin - #28
+
+###[0.6.1]
+ * Updates for typos in settings and official policy names - Gerben Welter - #19
+ * Support old-style file-loading - Jordan Wesolowski - #24
+
+###[0.6.0] - Adam Yohrling
+ * Added new Network security settings, Typo Fixes, Idempotency - #18
+
+###[0.5.2] - Ryan Russell-Yates
+ * Updated all ruby files to UTF-8 forced encoding.
+
+###[0.4.1] - Adam Yohrling
+ * Fixed Issue 3 - undefined method error for 'Network access: Let Everyone permissions apply to
+ anonymous users' setting
+
+###[0.4.0] - Adam Yohrling
+ * Added support for ensuring Privilege Rights settings as absent
+
+###[0.3.2] - Adam Yohrling
+ * Added support for currently unset values
+ * Removed duplicate and invalid 'initalize' method
+ * Cleaned out .DS_Store files that were in the repository
+ * Moved references for external methods to self.class in flush method and removed duplicate data
diff --git a/modules/utilities/windows/system/local_security_policy/Gemfile b/modules/utilities/windows/system/local_security_policy/Gemfile
new file mode 100644
index 000000000..c2d6fc26a
--- /dev/null
+++ b/modules/utilities/windows/system/local_security_policy/Gemfile
@@ -0,0 +1,22 @@
+source "https://rubygems.org"
+
+group :test do
+ gem "rake"
+ gem "puppet", ENV['PUPPET_VERSION'] || '~> 3.7.3'
+ gem "rspec-puppet", :git => 'https://github.com/rodjek/rspec-puppet.git'
+ gem "puppetlabs_spec_helper"
+ gem 'rspec-puppet-utils', :git => 'https://github.com/Accuity/rspec-puppet-utils.git'
+ # there seems to be a bug with puppet-blacksmith and metadata-json-lint
+ # removing metadata for now
+ gem "metadata-json-lint"
+ gem 'puppet-syntax'
+ gem 'puppet-lint'
+ gem 'awesome_print'
+end
+
+group :development do
+ gem "travis"
+ gem "travis-lint"
+ gem "puppet-blacksmith"
+end
+
diff --git a/modules/utilities/windows/system/local_security_policy/LICENSE b/modules/utilities/windows/system/local_security_policy/LICENSE
new file mode 100644
index 000000000..d64569567
--- /dev/null
+++ b/modules/utilities/windows/system/local_security_policy/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/modules/utilities/windows/system/local_security_policy/README.md b/modules/utilities/windows/system/local_security_policy/README.md
new file mode 100755
index 000000000..98c95ed5f
--- /dev/null
+++ b/modules/utilities/windows/system/local_security_policy/README.md
@@ -0,0 +1,272 @@
+# Puppet Local Security Policy
+
+created by Paul Cannon at email paulscannon at gmail dot com
+
+forked and updated by Adam Yohrling at email aryohrling at gmail dot com
+
+## Local_security_policy features
+Configure, local security policy (LSP) for windows servers.
+LSP is key to a baseline configuration of the following security features:
+### Account Policy
+ * Password Policy
+ * Account Lockout Policy
+### Local Policy
+ * Audit Policy
+ * User Rights Assignment
+ * Security Options
+ * Registry Values
+
+
+This module uses types and providers to list, update, validate settings
+
+## Use
+The title and name of the resources is exact match of what is in secedit GUI. If you are uncertain of the setting name and values just user 'resource' to pipe them all into a file and make adjustments as necessary.
+The block will look like this
+```
+local_security_policy { 'Audit account logon events': <- Title / Name
+ ensure => present, <- Always present
+ policy_setting => "AuditAccountLogon", <- The secedit file key. Informational purposes only, not for use in manifest definitions
+ policy_type => "Event Audit", <- The secedit file section, Informational purposes only, not for use in manifest definitions
+ policy_value => 'Success,Failure', <- Values
+}
+```
+
+
+### Listing all settings
+Show all local_security_policy resources available on server
+```
+puppet resource local_security_policy
+```
+Show a single local_security_policy resources available on server
+```
+puppet resource local_security_policy 'Maximum password age'
+```
+
+### More examples
+Example Password Policy
+```
+local_security_policy { 'Maximum password age':
+ ensure => present,
+ policy_value => '90',
+}
+```
+
+Example Audit Policy
+```
+local_security_policy { 'Audit account logon events':
+ ensure => present,
+ policy_value => 'Success,Failure',
+}
+```
+
+Example User Rights Policy
+```
+local_security_policy { 'Allow log on locally':
+ ensure => present,
+ policy_value => '90',
+}
+```
+Example Security Settings
+```
+local_security_policy { 'System cryptography: Use FIPS compiant algorithms for encryption, hashing, and signing':
+ ensure => present,
+ policy_value => 1 ,
+}
+```
+
+### Full list of settings available
+ Access Credential Manager as a trusted caller
+ Access this computer from the network
+ Account lockout duration
+ Account lockout threshold
+ Accounts: Block Microsoft accounts
+ Accounts: Limit local account use of blank passwords to console logon only
+ Accounts: Rename administrator account
+ Accounts: Rename guest account
+ Accounts: Require Login to Change Password
+ Act as part of the operating system
+ Add workstations to domain
+ Adjust memory quotas for a process
+ Allow log on locally
+ Allow log on through Remote Desktop Services
+ Audit account logon events
+ Audit account management
+ Audit: Audit the access of global system objects
+ Audit: Audit the use of Backup and Restore privilege
+ Audit directory service access
+ Audit: Force audit policy subcategory settings (Windows Vista or later) to override audit policy category settings
+ Audit logon events
+ Audit object access
+ Audit policy change
+ Audit privilege use
+ Audit process tracking
+ Audit: Shut down system immediately if unable to log security audits
+ Audit system events
+ Back up files and directories
+ Bypass traverse checking
+ Change the system time
+ Change the time zone
+ Create a pagefile
+ Create a token object
+ Create global objects
+ Create permanent shared objects
+ Create symbolic links
+ Debug programs
+ Deny access to this computer from the network
+ Deny log on as a batch job
+ Deny log on as a service
+ Deny log on locally
+ Deny log on through Remote Desktop Services
+ Devices: Allowed to format and eject removable media
+ Devices: Allow undock without having to log on
+ Devices: Prevent users from installing printer drivers
+ Devices: Restrict CD-ROM access to locally logged-on user only
+ Devices: Restrict floppy access to locally logged-on user only
+ Domain member: Digitally encrypt or sign secure channel data (always)
+ Domain member: Digitally encrypt secure channel data (when possible)
+ Domain member: Digitally sign secure channel data (when possible)
+ Domain member: Disable machine account password changes
+ Domain member: Maximum machine account password age
+ Domain member: Require strong (Windows 2000 or later) session key
+ EnableAdminAccount
+ Enable computer and user accounts to be trusted for delegation
+ Enforce password history
+ Force shutdown from a remote system
+ Generate security audits
+ Impersonate a client after authentication
+ Increase a process working set
+ Increase scheduling priority
+ Interactive logon: Display user information when the session is locked
+ Interactive logon: Do not display last user name
+ Interactive logon: Do not require CTRL+ALT+DEL
+ Interactive logon: Machine account lockout threshold
+ Interactive logon: Machine inactivity limit
+ Interactive logon: Message text for users attempting to log on
+ Interactive logon: Message title for users attempting to log on
+ Interactive logon: Number of previous logons to cache (in case domain controller is not available)
+ Interactive logon: Prompt user to change password before expiration
+ Interactive logon: Require Domain Controller authentication to unlock workstation
+ Interactive logon: Require smart card
+ Interactive logon: Smart card removal behavior
+ Load and unload device drivers
+ Lock pages in memory
+ Log on as a batch job
+ Log on as a service
+ Manage auditing and security log
+ Maximum password age
+ Microsoft network client: Digitally sign communications (always)
+ Microsoft network client: Digitally sign communications (if server agrees)
+ Microsoft network client: Send unencrypted password to third-party SMB servers
+ Microsoft network server: Amount of idle time required before suspending session
+ Microsoft network server: Digitally sign communications (always)
+ Microsoft network server: Digitally sign communications (if client agrees)
+ Microsoft network server: Disconnect clients when logon hours expire
+ Microsoft network server: Server SPN target name validation level
+ Minimum password age
+ Minimum password length
+ Modify an object label
+ Modify firmware environment values
+ Network access: Allow anonymous SID/name translation
+ Network access: Do not allow anonymous enumeration of SAM accounts
+ Network access: Do not allow anonymous enumeration of SAM accounts and shares
+ Network access: Do not allow storage of passwords and credentials for network authentication
+ Network access: Let Everyone permissions apply to anonymous users
+ Network access: Named Pipes that can be accessed anonymously
+ Network access: Remotely accessible registry paths
+ Network access: Remotely accessible registry paths and sub-paths
+ Network access: Restrict anonymous access to Named Pipes and Shares
+ Network access: Shares that can be accessed anonymously
+ Network access: Sharing and security model for local accounts
+ Network security: All Local System to use computer identity for NTLM
+ Network security: Do not store LAN Manager hash value on next password change
+ Network security: Force logoff when logon hours expire
+ Network security: LAN Manager authentication level
+ Network security: LDAP client signing requirements
+ Network security: Minimum session security for NTLM SSP based (including secure RPC) clients
+ Network security: Minimum session security for NTLM SSP based (including secure RPC) servers
+ Password must meet complexity requirements
+ Perform volume maintenance tasks
+ Profile single process
+ Profile system performance
+ Recovery console: Allow automatic administrative logon
+ Recovery console: Allow floppy copy and access to all drives and all folders
+ Remove computer from docking station
+ Replace a process level token
+ Reset account lockout counter after
+ Restore files and directories
+ Shutdown: Allow system to be shut down without having to log on
+ Shutdown: Clear virtual memory pagefile
+ Shut down the system
+ Store passwords using reversible encryption
+ Synchronize directory service data
+ System cryptography: Force strong key protection for user keys stored on the computer
+ System cryptography: Use FIPS compliant algorithms for encryption, hashing, and signing
+ System objects: Require case insensitivity for non-Windows subsystems
+ System objects: Strengthen default permissions of internal system objects (e.g., Symbolic Links)
+ System settings: Optional subsystems
+ System settings: Use Certificate Rules on Windows Executables for Software Restriction Policies
+ Take ownership of files or other objects
+ User Account Control: Admin Approval Mode for the Built-in Administrator account
+ User Account Control: Allow UIAccess applications to prompt for elevation without using the secure desktop
+ User Account Control: Behavior of the elevation prompt for administrators in Admin Approval Mode
+ User Account Control: Behavior of the elevation prompt for standard users
+ User Account Control: Detect application installations and prompt for elevation
+ User Account Control: Only elevate executables that are signed and validated
+ User Account Control: Only elevate UIAccess applications that are installed in secure locations
+ User Account Control: Run all administrators in Admin Approval Mode
+ User Account Control: Switch to the secure desktop when prompting for elevation
+ User Account Control: Virtualize file and registry write failures to per-user locations
+
+
+
+## How this works
+The local_security_policy works by using `secedit /export` to export a list of currently set policies. The module will then
+take the user defined resource and compare the values against the exported policies. If the values on the system do not match
+the defined resource, the module will run `secedit /configure` to configure the policy on the system. If the policy already
+exists on the system no change will be made.
+
+In order to make setting these polices easier, this module has extracted some of the difficult to lookup or remember pieces
+of a policy and placed them in a map for easy translation and value conversion. This means that you only need to remember the user
+instead of the sid value, as well as the policy description instead of the special key that needs to be set. The mappings
+below define how this translation works. If there is no map for your policy you will need to add to `lib/puppet_x/lsp/security_policy.rb`
+
+```
+'Accounts: Rename administrator account' => {
+ :name => 'NewAdministratorName',
+ :policy_type => 'System Access',
+ :data_type => :quoted_string
+ },
+ 'Recovery console: Allow floppy copy and access to all drives and all folders' => {
+ :name => 'MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Setup\RecoveryConsole\SetCommand',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+```
+
+The key `Accounts: Rename administrator account ` in the first hash is what the user will define as the name in the resource name.
+Instead of remembering the policy name, the description will help us remember what the policy is for. When defining new policy
+maps you will need to define the key, name, policy_type, and optionally, data_type or reg_type.
+
+Currently for data_type there is only `:quoted_string`. However, for reg_type(integer value) there are many values which are listed below:
+
+```
+ REG_NONE 0
+ REG_SZ 1
+ REG_EXPAND_SZ 2
+ REG_BINARY 3
+ REG_DWORD 4
+ REG_DWORD_LITTLE_ENDIAN 4
+ REG_DWORD_BIG_ENDIAN 5
+ REG_LINK 6
+ REG_MULTI_SZ 7
+ REG_RESOURCE_LIST 8
+ REG_FULL_RESOURCE_DESCRIPTOR 9
+ REG_RESOURCE_REQUIREMENTS_LIST 10
+ REG_QWORD 11
+ REG_QWORD_LITTLE_ENDIAN 11
+```
+## Commands Used
+
+## TODO: Future release
+* Handle unsupported policies
+* Validate users in active directory are being handled.
diff --git a/modules/utilities/windows/system/local_security_policy/Rakefile b/modules/utilities/windows/system/local_security_policy/Rakefile
new file mode 100644
index 000000000..9001a0611
--- /dev/null
+++ b/modules/utilities/windows/system/local_security_policy/Rakefile
@@ -0,0 +1,80 @@
+require 'puppetlabs_spec_helper/rake_tasks'
+require 'puppet-lint/tasks/puppet-lint'
+require 'puppet-syntax/tasks/puppet-syntax'
+
+# These two gems aren't always present, for instance
+# on Travis with --without development
+begin
+ require 'puppet_blacksmith/rake_tasks'
+ Blacksmith::RakeTask.new do |t|
+ t.tag_pattern = "v%s" # Use a custom pattern with git tag. %s is replaced with the version number.
+ end
+rescue LoadError
+end
+
+PuppetLint.configuration.relative = true
+PuppetLint.configuration.send("disable_80chars")
+PuppetLint.configuration.log_format = "%{path}:%{linenumber}:%{check}:%{KIND}:%{message}"
+PuppetLint.configuration.fail_on_warnings = true
+
+# Forsake support for Puppet 2.6.2 for the benefit of cleaner code.
+# http://puppet-lint.com/checks/class_parameter_defaults/
+PuppetLint.configuration.send('disable_class_parameter_defaults')
+# http://puppet-lint.com/checks/class_inherits_from_params_class/
+PuppetLint.configuration.send('disable_class_inherits_from_params_class')
+
+exclude_paths = [
+ "pkg/**/*",
+ "vendor/**/*",
+ "spec/**/*",
+]
+PuppetLint.configuration.ignore_paths = exclude_paths
+PuppetSyntax.exclude_paths = exclude_paths
+
+task :metadata do
+ sh "metadata-json-lint metadata.json"
+end
+
+desc "Run syntax, lint, and spec tests."
+task :test => [
+ :syntax,
+ :lint,
+ :spec,
+ :metadata,
+ ]
+def io_popen(command)
+ IO.popen(command) do |io|
+ io.each do |line|
+ print line
+ yield line if block_given?
+ end
+ end
+end
+
+desc 'Vagrant VM power up and provision'
+task :vagrant_up, [:manifest, :hostname] do |t, args|
+ args.with_defaults(:manifest => 'init.pp', :hostname => '')
+ Rake::Task['spec_prep'].execute
+ ENV['VAGRANT_MANIFEST'] = args[:manifest]
+ provision = false
+ io_popen("vagrant up #{args[:hostname]}") do |line|
+ provision = true if line =~ /is already running./
+ end
+ io_popen("vagrant provision #{args[:hostname]}") if provision
+end
+
+# Cleanup vagrant environment
+desc 'Vagrant VM shutdown and fixtures cleanup'
+task :vagrant_destroy do
+ Rake::Task['spec_prep'].execute
+ `vagrant destroy -f`
+ Rake::Task['spec_clean'].execute
+end
+
+desc 'Return list of policy names'
+task :policy_list do
+ require_relative "lib/puppet_x/lsp/security_policy"
+ puts "\nCurrent Policy List"
+ puts "----------------------------------------------"
+ SecurityPolicy.lsp_mapping.keys.sort.each {|key| puts "#{key}\n"}
+end
\ No newline at end of file
diff --git a/modules/utilities/windows/system/local_security_policy/Vagrantfile b/modules/utilities/windows/system/local_security_policy/Vagrantfile
new file mode 100644
index 000000000..608e92353
--- /dev/null
+++ b/modules/utilities/windows/system/local_security_policy/Vagrantfile
@@ -0,0 +1,94 @@
+def vm(opt)
+ module_name = opt.fetch(:module).to_s || raise(ArgumentError, 'Must provide puppet module name')
+ hostname = opt.fetch(:hostname, module_name).to_s
+ memory = opt.fetch(:memory, 512)
+ cpu = opt.fetch(:cpu, 1)
+ box = opt.fetch(:box).to_s || raise(ArgumentError, 'Must provide box type.')
+ url = opt.fetch(:url, '').to_s
+ os_type = opt[:os_type] || opt[:type] || :linux
+ gui = opt.fetch(:gui, false)
+ ports = Array(opt.fetch(:port, []))
+ iso = opt.fetch(:iso, nil)
+ proj_root = File.expand_path(File.join(File.dirname(__FILE__)))
+ fixture_modules = File.join(proj_root, 'spec', 'fixtures', 'modules')
+
+ Vagrant.configure('2') do |conf|
+
+ # forward all the ports
+ ports.each do |p|
+ conf.vm.network(:forwarded_port, guest: p, host: p, auto_correct: true)
+ end
+
+ if os_type == :windows
+ conf.ssh.username = 'vagrant'
+ conf.winrm.username = 'vagrant'
+ conf.winrm.password = 'vagrant'
+ end
+
+ conf.vm.define hostname.to_sym do |mod|
+ mod.vm.box = box
+ mod.vm.box_url = url
+
+ if os_type == :windows
+ mod.vm.guest = :windows
+ mod.vm.communicator = 'winrm'
+ mod.vm.synced_folder './' , "/ProgramData/PuppetLabs/puppet/etc/modules/#{module_name}"
+ mod.vm.synced_folder 'spec/fixtures/modules' , '/temp/modules'
+ else
+ mod.vm.synced_folder './', "/etc/puppet/modules/#{module_name}"
+ mod.vm.synced_folder 'spec/fixtures/modules', '/tmp/puppet/modules'
+ end
+
+ mod.vm.hostname = hostname
+
+ mod.vm.provider :vmware_fusion do |f|
+ f.gui = gui
+ f.vmx['displayName'] = hostname
+ f.vmx['memsize'] = memory
+ f.vmx['numvcpus'] = cpu
+ if iso
+ f.vmx['ide1:0.devicetype'] = "cdrom-image"
+ f.vmx['ide1:0.filename'] = iso
+ end
+ end
+
+ mod.vm.provider :vmware_workstation do |f|
+ f.gui = gui
+ f.vmx['displayName'] = hostname
+ f.vmx['memsize'] = memory
+ f.vmx['numvcpus'] = cpu
+ if iso
+ f.vmx['ide1:0.devicetype'] = "cdrom-image"
+ f.vmx['ide1:0.filename'] = iso
+ end
+ end
+
+ mod.vm.provider :virtualbox do |v|
+ v.gui = gui
+ v.name = hostname
+ v.memory = memory
+ v.cpus = cpu
+ end
+
+ if os_type == :windows
+ manifest = ENV['VAGRANT_MANIFEST'] || 'init.pp'
+ # mod.vm.provision :shell, :inline => "iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))"
+ # mod.vm.provision :shell, :inline => "choco -y install puppet"
+ mod.vm.provision :shell, :inline => "puppet apply --modulepath 'C:/ProgramData/PuppetLabs/puppet/etc/modules;C:/temp/modules' --verbose C:/ProgramData/PuppetLabs/puppet/etc/modules/#{module_name}/tests/#{manifest}"
+ else
+ mod.vm.provision :puppet do |p|
+ p.manifests_path = 'tests'
+ p.hiera_config_path = File.join(fixture_modules, 'hieradata', 'hiera.yaml')
+ p.manifest_file = ENV['VAGRANT_MANIFEST'] || 'init.pp'
+ #p.module_path = fixture_modules
+ # because of how symlinks are handled via the spec_helper we are forced to mount the modules is different locations
+ # otherwise we could just use the above option
+ p.options = '--modulepath="/etc/puppet/modules:/tmp/puppet/modules"'
+ end
+ end
+ end
+ end
+end
+
+vm :hostname => 'win2012r2', :module => 'profiles', :box => 'opentable/win-2012r2-standard-amd64-nocm', :url => 'opentable/win-2012r2-standard-amd64-nocm', :os_type => :windows, :cpu => 1, :memory => 4096, :gui => true
+vm :hostname => 'win2008r2', :module => 'profiles', :box => 'opentable/win-2008r2-standard-amd64-nocm', :url => 'opentable/win-2008r2-standard-amd64-nocm', :os_type => :windows, :cpu => 1, :memory => 4096, :gui => true
diff --git a/modules/utilities/windows/system/local_security_policy/checksums.json b/modules/utilities/windows/system/local_security_policy/checksums.json
new file mode 100644
index 000000000..2b0d137f6
--- /dev/null
+++ b/modules/utilities/windows/system/local_security_policy/checksums.json
@@ -0,0 +1,26 @@
+{
+ "CHANGELOG.md": "070da7699591cd3bf94005b02d8f7a20",
+ "Gemfile": "578005b552456cd33b30cee6889f28f6",
+ "Gemfile.lock": "09d3a9aee76de7f0b0d3d094d0eb6f73",
+ "LICENSE": "3b83ef96387f14655fc854ddc3c6bd57",
+ "README.md": "0ab316b7a948dd42829298a86027306a",
+ "Rakefile": "14264e48a158ab7357606c7df61ddd79",
+ "Vagrantfile": "65f7d6ebc54ade8fc8828e3bcb75ab0e",
+ "lib/puppet/provider/local_security_policy/policy.rb": "eee8076ccc563dd96e9cecfbecea7694",
+ "lib/puppet/type/local_security_policy.rb": "66875e9cc349beb0edf5ca3820f04373",
+ "lib/puppet_x/lsp/security_policy.rb": "74c4dd4da3a3328fe698f363cef66823",
+ "lib/puppet_x/twp/inifile.rb": "d9e1e62229899e191836fbc5254555d2",
+ "manifests/init.pp": "17a002bbf7698087525f28a3bf5f17bf",
+ "metadata.json": "c85f6fb4d24e80b7b67e30e0fa301d90",
+ "spec/classes/local_security_policy_spec.rb": "309ccc462f3856065a1bc8e7fda330cb",
+ "spec/fixtures/unit/group.txt": "7139a4cbda2e7294724f44f07f1f99e8",
+ "spec/fixtures/unit/secedit.inf": "d38bcd0c95fd3d62449db03c7b48d996",
+ "spec/fixtures/unit/short_secedit.inf": "de3be6848b6c737fdaf9a3dad424412e",
+ "spec/fixtures/unit/useraccount.txt": "c6add66cc0bab0d8dc96c2eaa131fa6f",
+ "spec/shared_contexts.rb": "7ec4683e370ecbe6795d351079a6a544",
+ "spec/spec_helper.rb": "3639a702dfd49bde5118175365a257b8",
+ "spec/unit/puppet/provider/local_security_policy/policy_spec.rb": "58432a207358b0c54ec67c450d2f3246",
+ "spec/unit/puppet/provider/local_security_policy/security_policy_spec.rb": "5b37ef57f96d1d63e4ea73c8e824a1b3",
+ "spec/unit/puppet/type/local_security_policy/local_security_policy_spec.rb": "e17edc0386c8a4da0b49b19ba7e6e754",
+ "tests/init.pp": "fbb38c252ace282cb7aa2c7a1a87c680"
+}
\ No newline at end of file
diff --git a/modules/utilities/windows/system/local_security_policy/lib/puppet/provider/local_security_policy/policy.rb b/modules/utilities/windows/system/local_security_policy/lib/puppet/provider/local_security_policy/policy.rb
new file mode 100644
index 000000000..506fe3265
--- /dev/null
+++ b/modules/utilities/windows/system/local_security_policy/lib/puppet/provider/local_security_policy/policy.rb
@@ -0,0 +1,185 @@
+#encoding: UTF-8
+require 'fileutils'
+
+begin
+ require "puppet_x/twp/inifile"
+ require "puppet_x/lsp/security_policy"
+rescue LoadError => detail
+ require 'pathname' # JJM WORK_AROUND #14073
+ mod = Puppet::Module.find('local_security_policy', Puppet[:environment].to_s)
+ if mod
+ require File.join(mod.path, 'lib/puppet_x/twp/inifile')
+ require File.join(mod.path, 'lib/puppet_x/lsp/security_policy')
+ else # received nil, fallback to old style
+ module_base = Pathname.new(__FILE__).dirname
+ require File.join(module_base, '../../../', 'puppet_x/twp/inifile')
+ require File.join(module_base, '../../../', 'puppet_x/lsp/security_policy')
+ end
+end
+
+Puppet::Type.type(:local_security_policy).provide(:policy) do
+ desc 'Puppet type that models the local security policy'
+
+ #
+ # TODO Finalize the registry key settings
+ # TODO Add in registry value translation (ex: 1=enable 0=disable)
+ # TODO Implement self.post_resource_eval (need to collect all resource updates the run secedit to make one call)
+ # limit access to windows hosts only
+ confine :operatingsystem => :windows
+ # limit access to systems with these commands since this is the tools we need
+ commands :wmic => 'wmic', :secedit => 'secedit'
+
+ mk_resource_methods
+
+ # export the policy settings to the specified file and return the filename
+ def self.export_policy_settings(inffile=nil)
+ inffile ||= temp_file
+ secedit(['/export', '/cfg', inffile, '/quiet'])
+ inffile
+ end
+
+ # export and then read the policy settings from a file into a inifile object
+ # caches the IniFile object during the puppet run
+ def self.read_policy_settings(inffile=nil)
+ inffile ||= temp_file
+ unless @file_object
+ export_policy_settings(inffile)
+ File.open inffile, 'r:IBM437' do |file|
+ # remove /r/n and remove the BOM
+ inffile_content = file.read.force_encoding('utf-16le').encode('utf-8', :universal_newline => true).gsub("\xEF\xBB\xBF", '')
+ @file_object ||= PuppetX::IniFile.new(:content => inffile_content)
+ end
+ end
+ @file_object
+ end
+
+ # converts any values that might be of a certain type specified in the mapping
+ # converts everything to a string
+ # returns the value
+ def self.fixup_value(value, type)
+ value = value.to_s.strip
+ case type
+ when :quoted_string
+ value = "\"#{value}\""
+ end
+ value
+ end
+
+ # exports the current list of policies into a file and then parses that file into
+ # provider instances. If an item is found on the system but not in the lsp_mapping,
+ # that policy is not supported only because we cannot match the description
+ # furthermore, if a policy is in the mapping but not in the system we would consider
+ # that resource absent
+ def self.instances
+ settings = []
+ inf = read_policy_settings
+ # need to find the policy, section_header, policy_setting, policy_value and reg_type
+ inf.each do |section, parameter_name, parameter_value|
+ next if section == 'Unicode'
+ next if section == 'Version'
+ begin
+ ensure_value = parameter_value.nil? ? :absent : :present
+ policy_desc, policy_values = SecurityPolicy.find_mapping_from_policy_name(parameter_name)
+ policy_hash = {
+ :name => policy_desc,
+ :ensure => ensure_value,
+ :policy_type => section ,
+ :policy_setting => parameter_name,
+ :policy_value => fixup_value(parameter_value, policy_values[:data_type])
+ }
+ inst = new(policy_hash)
+ settings << inst
+ rescue KeyError => e
+ Puppet.debug e.message
+ end
+ end
+ settings
+ end
+
+ # the flush method will be the last method called after applying all the other
+ # properties, by default nothing will be enabled or disabled unless the disable/enable are set to true
+ # if we ever move to a point were we can write all the settings via one big config file we
+ # would want to do that here.
+ def flush
+ begin
+ write_policy_to_system(resource.to_hash)
+ rescue KeyError => e
+ Puppet.debug e.message
+ # send helpful debug message to user here
+ end
+ @property_hash = resource.to_hash
+ end
+
+ def initialize(value={})
+ super(value)
+ @property_flush = {}
+ end
+
+ # create the resource and convert any user supplied values to computer terms
+ def create
+ # do everything in flush method
+ end
+
+ # this is currently not implemented correctly on purpose until we can figure out how to safely remove
+ def destroy
+ @property_hash[:ensure] = :absent
+ #Destroy not an option for now. LSP Settings should be set to something.
+ # we need some default destroy values in the mappings so we know ahead of time what to put unless the user supplies
+ # but this would just ensure a value the setting should go back to
+ end
+
+ def self.prefetch(resources)
+ policies = instances
+ resources.keys.each do |name|
+ if found_pol = policies.find { |pol| pol.name == name }
+ resources[name].provider = found_pol
+ end
+ end
+ end
+
+ def exists?
+ @property_hash[:ensure] == :present
+ end
+
+ # gets the property hash from the provider
+ def to_hash
+ instance_variable_get('@property_hash')
+ end
+
+ # required for easier mocking, this could be a Tempfile too
+ def self.temp_file
+ 'c:\\windows\\temp\\secedit.inf'
+ end
+
+ def temp_file
+ 'c:\\windows\\temp\\secedit.inf'
+ end
+
+ # writes out one policy at a time using the InfFile Class and secedit
+ def write_policy_to_system(policy_hash)
+ time = Time.now
+ time = time.strftime("%Y%m%d%H%M%S")
+ infout = "c:\\windows\\temp\\infimport-#{time}.inf"
+ sdbout = "c:\\windows\\temp\\sdbimport-#{time}.inf"
+ #logout = "c:\\windows\\temp\\logout-#{time}.inf"
+ status = nil
+ begin
+ # read the system state into the inifile object for easy variable setting
+ inf = PuppetX::IniFile.new
+ # these sections need to be here by default
+ inf["Version"] = {"signature"=>"$CHICAGO$", "Revision"=>1}
+ inf["Unicode"] = {"Unicode"=>"yes"}
+ section = policy_hash[:policy_type]
+ section_value = {policy_hash[:policy_setting] => policy_hash[:policy_value]}
+ # we can utilize the IniFile class to write out the data in ini format
+ inf[section] = section_value
+ inf.write(:filename => infout, :encoding => 'utf-8')
+ secedit(['/configure', '/db', sdbout, '/cfg',infout])
+ ensure
+ FileUtils.rm_f(temp_file)
+ FileUtils.rm_f(infout)
+ FileUtils.rm_f(sdbout)
+ #FileUtils.rm_f(logout)
+ end
+ end
+end
diff --git a/modules/utilities/windows/system/local_security_policy/lib/puppet/type/local_security_policy.rb b/modules/utilities/windows/system/local_security_policy/lib/puppet/type/local_security_policy.rb
new file mode 100644
index 000000000..6855667d0
--- /dev/null
+++ b/modules/utilities/windows/system/local_security_policy/lib/puppet/type/local_security_policy.rb
@@ -0,0 +1,112 @@
+#encoding: UTF-8
+begin
+ require "puppet_x/lsp/security_policy"
+rescue LoadError => detail
+ require 'pathname' # JJM WORK_AROUND #14073
+ mod = Puppet::Module.find('local_security_policy', Puppet[:environment].to_s)
+ if mod
+ require File.join(mod.path, 'lib/puppet_x/lsp/security_policy')
+ else # received nil, fallback to old style
+ module_base = Pathname.new(__FILE__).dirname
+ require File.join(module_base, '../../', 'puppet_x/lsp/security_policy')
+ end
+end
+
+Puppet::Type.newtype(:local_security_policy) do
+ @doc = 'Puppet type that models the local security policy'
+
+ ensurable
+
+ newparam(:name, :namevar => true) do
+ desc 'Local Security Setting Name. What you see it the GUI.'
+ validate do |value|
+ raise ArgumentError, "Invalid Policy name: #{value}" unless SecurityPolicy.valid_lsp?(value)
+ end
+ end
+
+ newproperty(:policy_type) do
+ newvalues('System Access','Event Audit','Privilege Rights','Registry Values', nil, '')
+ desc 'Local Security Policy Machine Name. What OS knows it by.'
+ defaultto do
+ begin
+ policy_hash = SecurityPolicy.find_mapping_from_policy_desc(resource[:name])
+ rescue KeyError => e
+ fail(e.message)
+ end
+ policy_hash[:policy_type]
+ end
+ # uses the resource name to perform a lookup of the defined policy and returns the policy type
+ munge do |value|
+ begin
+ policy_hash = SecurityPolicy.find_mapping_from_policy_desc(resource[:name])
+ rescue KeyError => e
+ fail(e.message)
+ end
+ policy_hash[:policy_type]
+ end
+ end
+
+ newproperty(:policy_setting) do
+
+ desc 'Local Security Policy Machine Name. What OS knows it by.'
+ defaultto do
+ begin
+ policy_hash = SecurityPolicy.find_mapping_from_policy_desc(resource[:name])
+ rescue KeyError => e
+ fail(e.message)
+ end
+ policy_hash[:name]
+ end
+ munge do |value|
+ begin
+ policy_hash = SecurityPolicy.find_mapping_from_policy_desc(resource[:name])
+ rescue KeyError => e
+ fail(e.message)
+ end
+ policy_hash[:name]
+ end
+ end
+
+ newproperty(:policy_value) do
+ desc 'Local Security Policy Setting Value'
+ validate do |value|
+ if value.nil? or value.empty?
+ raise ArgumentError("Value cannot be nil or empty")
+ end
+ case resource[:policy_type].to_s
+ when 'Privilege Rights'
+ # maybe validate some sort of user?
+ when 'Event Audit'
+ raise ArgumentError("Invalid Event type: #{value} for #{resource[:policy_value]}") unless SecurityPolicy::EVENT_TYPES.include?(value)
+ when 'Registry Values'
+ # maybe validate the value based on the datatype?
+
+ # REG_NONE 0
+ # REG_SZ 1
+ # REG_EXPAND_SZ 2
+ # REG_BINARY 3
+ # REG_DWORD 4
+ # REG_DWORD_LITTLE_ENDIAN 4
+ # REG_DWORD_BIG_ENDIAN 5
+ # REG_LINK 6
+ # REG_MULTI_SZ 7
+ # REG_RESOURCE_LIST 8
+ # REG_FULL_RESOURCE_DESCRIPTOR 9
+ # REG_RESOURCE_REQUIREMENTS_LIST 10
+ # REG_QWORD 11
+ # REG_QWORD_LITTLE_ENDIAN 11
+ end
+ end
+
+ munge do | value |
+ # need to convert policy values to designated types
+ case resource[:policy_type].to_s
+ when 'Registry Values'
+ # secedit values sometimes look like : "1,\"4\""
+ end
+ SecurityPolicy.convert_policy_value(resource.to_hash, value)
+ end
+ end
+end
+
+
diff --git a/modules/utilities/windows/system/local_security_policy/lib/puppet_x/lsp/security_policy.rb b/modules/utilities/windows/system/local_security_policy/lib/puppet_x/lsp/security_policy.rb
new file mode 100644
index 000000000..c725a5c65
--- /dev/null
+++ b/modules/utilities/windows/system/local_security_policy/lib/puppet_x/lsp/security_policy.rb
@@ -0,0 +1,925 @@
+#encoding: UTF-8
+require 'puppet/provider'
+
+class SecurityPolicy
+ attr_reader :wmic_cmd
+ EVENT_TYPES = ["Success,Failure", "Success", "Failure", "No auditing", 0, 1, 2, 3]
+
+ def initialize
+ # suppose to make an instance method for wmic
+ @wmic_cmd = Puppet::Provider::CommandDefiner.define('wmic', 'wmic', Puppet::Provider)
+ end
+
+ def wmic(args=[])
+ case args[0]
+ when 'useraccount'
+ @@useraccount ||= wmic_cmd.execute(args).force_encoding('utf-16le').encode('utf-8', :universal_newline => true).gsub("\xEF\xBB\xBF", '')
+ when 'group'
+ @@groupaccount ||= wmic_cmd.execute(args).force_encoding('utf-16le').encode('utf-8', :universal_newline => true).gsub("\xEF\xBB\xBF", '')
+ else
+ # will not cache
+ wmic_cmd.execute(args).force_encoding('utf-16le').encode('utf-8', :universal_newline => true).gsub("\xEF\xBB\xBF", '')
+ end
+ end
+
+ # collect all the local accounts using wmic
+ def local_accounts
+ ary = []
+ ["useraccount","group"].each do |lu|
+ wmic([lu, 'where', "(domain=\"#{ENV["COMPUTERNAME"]}\")", 'get', 'name,sid', '/format:csv']).split("\n").each do |line|
+ next if line =~ /Node/
+ if line.include? ","
+ ary << line.strip.split(",")
+ end
+ end
+ end
+ ary
+ end
+
+ def user_sid_array
+ @user_sid_array ||= local_accounts + builtin_accounts
+ end
+
+ def user_to_sid(value)
+ sid = user_sid_array.find do |home,user,sid|
+ user == value
+ end
+ unless sid.nil?
+ '*' + sid[2]
+ else
+ value
+ end
+ end
+
+ # convert the sid to a user
+ def sid_to_user(value)
+ value = value.gsub(/(^\*)/ , '')
+ user = user_sid_array.find do |home,user,sid|
+ value == sid
+ end
+ unless user.nil?
+ user[1]
+ else
+ value
+ end
+ end
+
+ def convert_privilege_right(ensure_value, policy_value)
+ # we need to convert users to sids first
+ if ensure_value.to_s == 'absent'
+ pv = ''
+ else
+ sids = Array.new
+ policy_value.split(",").sort.each do |suser|
+ suser.strip!
+ sids << user_to_sid(suser)
+ end
+ pv = sids.sort.join(",")
+ end
+ end
+
+ # converts the policy value inside the policy hash to conform to the secedit standards
+ def convert_policy_hash(policy_hash)
+ case policy_hash[:policy_type]
+ when 'Privilege Rights'
+ value = convert_privilege_right(policy_hash[:ensure], policy_hash[:policy_value])
+ when 'Event Audit'
+ value = event_to_audit_id(policy_hash[:policy_value])
+ when 'Registry Values'
+ value = SecurityPolicy.convert_registry_value(policy_hash[:name], policy_hash[:policy_value])
+ else
+ value = policy_hash[:policy_value]
+ end
+ policy_hash[:policy_value] = value
+ policy_hash
+ end
+
+ def builtin_accounts
+ # more accounts and SIDs can be found at https://support.microsoft.com/en-us/kb/243330
+ ary = [
+ ["","NULL","S-1-0"],
+ ["","NOBODY","S-1-0-0"],
+ ["","EVERYONE","S-1-1-0"],
+ ["","LOCAL","S-1-2-0"],
+ ["","CONSOLE_LOGON","S-1-2-1"],
+ ["","CREATOR_OWNER","S-1-3-0"],
+ ["","CREATER_GROUP","S-1-3-1"],
+ ["","OWNER_SERVER","S-1-3-2"],
+ ["","GROUP_SERVER","S-1-3-3"],
+ ["","OWNER_RIGHTS","S-1-3-4"],
+ ["","NT_AUTHORITY","S-1-5"],
+ ["","DIALUP","S-1-5-1"],
+ ["","NETWORK","S-1-5-2"],
+ ["","BATCH","S-1-5-3"],
+ ["","INTERACTIVE","S-1-5-4"],
+ ["","SERVICE","S-1-5-6"],
+ ["","ANONYMOUS","S-1-5-7"],
+ ["","PROXY","S-1-5-8"],
+ ["","ENTERPRISE_DOMAIN_CONTROLLERS","S-1-5-9"],
+ ["","PRINCIPAAL_SELF","S-1-5-10"],
+ ["","AUTHENTICATED_USERS","S-1-5-11"],
+ ["","RESTRICTED_CODE","S-1-5-12"],
+ ["","TERMINAL_SERVER_USER","S-1-5-13"],
+ ["","REMOTE_INTERACTIVE_LOGON","S-1-5-14"],
+ ["","THIS_ORGANIZATION","S-1-5-15"],
+ ["","IUSER","S-1-5-17"],
+ ["","LOCAL_SYSTEM","S-1-5-18"],
+ ["","LOCAL_SERVICE","S-1-5-19"],
+ ["","NETWORK_SERVICE","S-1-5-20"],
+ ["","COMPOUNDED_AUTHENTICATION","S-1-5-21-0-0-0-496"],
+ ["","CLAIMS_VALID","S-1-5-21-0-0-0-497"],
+ ["","BUILTIN_ADMINISTRATORS","S-1-5-32-544"],
+ ["","BUILTIN_USERS","S-1-5-32-545"],
+ ["","BUILTIN_GUESTS","S-1-5-32-546"],
+ ["","POWER_USERS","S-1-5-32-547"],
+ ["","ACCOUNT_OPERATORS","S-1-5-32-548"],
+ ["","SERVER_OPERATORS","S-1-5-32-549"],
+ ["","PRINTER_OPERATORS","S-1-5-32-550"],
+ ["","BACKUP_OPERATORS","S-1-5-32-551"],
+ ["","REPLICATOR","S-1-5-32-552"],
+ ["","ALIAS_PREW2KCOMPACC","S-1-5-32-554"],
+ ["","REMOTE_DESKTOP","S-1-5-32-555"],
+ ["","NETWORK_CONFIGURATION_OPS","S-1-5-32-556"],
+ ["","INCOMING_FOREST_TRUST_BUILDERS","S-1-5-32-557"],
+ ["","PERMON_USERS","S-1-5-32-558"],
+ ["","PERFLOG_USERS","S-1-5-32-559"],
+ ["","WINDOWS_AUTHORIZATION_ACCESS_GROUP","S-1-5-32-560"],
+ ["","TERMINAL_SERVER_LICENSE_SERVERS","S-1-5-32-561"],
+ ["","DISTRIBUTED_COM_USERS","S-1-5-32-562"],
+ ["","IIS_USERS","S-1-5-32-568"],
+ ["","CRYPTOGRAPHIC_OPERATORS","S-1-5-32-569"],
+ ["","EVENT_LOG_READERS","S-1-5-32-573"],
+ ["","CERTIFICATE_SERVICE_DCOM_ACCESS","S-1-5-32-574"],
+ ["","RDS_REMOTE_ACCESS_SERVERS","S-1-5-32-575"],
+ ["","RDS_ENDPOINT_SERVERS","S-1-5-32-576"],
+ ["","RDS_MANAGEMENT_SERVERS","S-1-5-32-577"],
+ ["","HYPER_V_ADMINS","S-1-5-32-578"],
+ ["","ACCESS_CONTROL_ASSISTANCE_OPS","S-1-5-32-579"],
+ ["","REMOTE_MANAGEMENT_USERS","S-1-5-32-580"],
+ ["","WRITE_RESTRICTED_CODE","S-1-5-32-558"],
+ ["","NTLM_AUTHENTICATION","S-1-5-64-10"],
+ ["","SCHANNEL_AUTHENTICATION","S-1-5-64-14"],
+ ["","DIGEST_AUTHENTICATION","S-1-5-64-21"],
+ ["","THIS_ORGANIZATION_CERTIFICATE","S-1-5-65-1"],
+ ["","NT_SERVICE","S-1-5-80"],
+ ["","NT_SERVICE\\ALL_SERVICES","S-1-5-80-0"],
+ ["","NT_SERVICE\\WdiServiceHost","S-1-5-80-3139157870-2983391045-3678747466-658725712-1809340420"],
+ ["","USER_MODE_DRIVERS","S-1-5-84-0-0-0-0-0"],
+ ["","LOCAL_ACCOUNT","S-1-5-113"],
+ ["","LOCAL_ACCOUNT_AND_MEMBER_OF_ADMINISTRATORS_GROUP","S-1-5-114"],
+ ["","OTHER_ORGANIZATION","S-1-5-1000"],
+ ["","ALL_APP_PACKAGES","S-1-15-2-1"],
+ ["","ML_UNTRUSTED","S-1-16-0"],
+ ["","ML_LOW","S-1-16-4096"],
+ ["","ML_MEDIUM","S-1-16-8192"],
+ ["","ML_MEDIUM_PLUS","S-1-16-8448"],
+ ["","ML_HIGH","S-1-15-12288"],
+ ["","ML_SYSTEM","S-1-16-16384"],
+ ["","ML_PROTECTED_PROCESS","S-1-16-20480"],
+ ["","AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY","S-1-18-1"],
+ ["","SERVICE_ASSERTED_IDENTITY","S-1-18-2"]
+ ]
+ ary
+ end
+
+ # Converts a event number to a word
+ def self.event_audit_mapper(policy_value)
+ case policy_value.to_s
+ when 3
+ return "Success,Failure"
+ when 2
+ return "Failure"
+ when 1
+ return "Success"
+ else
+ return "No auditing"
+ end
+ end
+
+ # Converts a event number to a word
+ def self.event_to_audit_id(event_audit_name)
+ case event_audit_name
+ when "Success,Failure"
+ return 3
+ when "Failure"
+ return 2
+ when "Success"
+ return 1
+ when 'No auditing'
+ return 0
+ else
+ return event_audit_name
+ end
+ end
+
+ # returns the key and hash value given the policy name
+ def self.find_mapping_from_policy_name(name)
+ key, value = lsp_mapping.find do |key,hash|
+ hash[:name] == name
+ end
+ unless key && value
+ raise KeyError, "#{name} is not a valid policy"
+ end
+ return key, value
+ end
+
+ # returns the key and hash value given the policy desc
+ def self.find_mapping_from_policy_desc(desc)
+ name = desc.downcase
+ value = nil
+ key, value = lsp_mapping.find do |key, hash|
+ key.downcase == name
+ end
+ unless value
+ raise KeyError, "#{desc} is not a valid policy"
+ end
+ return value
+ end
+
+ def self.valid_lsp?(name)
+ lsp_mapping.keys.include?(name)
+ end
+
+ def self.convert_registry_value(name, value)
+ value = value.to_s
+ return value if value.split(',').count > 1
+ policy_hash = find_mapping_from_policy_desc(name)
+ "#{policy_hash[:reg_type]},#{value}"
+ end
+
+ # converts the policy value to machine values
+ def self.convert_policy_value(policy_hash, value)
+ sp = SecurityPolicy.new
+ # I would rather not have to look this info up, but the type code will not always have this info handy
+ # without knowing the policy type we can't figure out what to convert
+ policy_type = find_mapping_from_policy_desc(policy_hash[:name])[:policy_type]
+ case policy_type.to_s
+ when 'Privilege Rights'
+ sp.convert_privilege_right(policy_hash[:ensure], value)
+ when 'Event Audit'
+ event_to_audit_id(value)
+ when 'Registry Values'
+ # convert the value to a datatype/value
+ convert_registry_value(policy_hash[:name], value)
+ else
+ value
+ end
+ end
+
+ def self.lsp_mapping
+ @lsp_mapping ||= {
+ # Password policy Mappings
+ 'Enforce password history' => {
+ :name => 'PasswordHistorySize',
+ :policy_type => 'System Access',
+ },
+ 'Maximum password age' => {
+ :name => 'MaximumPasswordAge',
+ :policy_type => 'System Access',
+ },
+ 'Minimum password age' => {
+ :name => 'MinimumPasswordAge',
+ :policy_type => 'System Access',
+ },
+ 'Minimum password length' => {
+ :name => 'MinimumPasswordLength',
+ :policy_type => 'System Access',
+ },
+ 'Password must meet complexity requirements' => {
+ :name => 'PasswordComplexity',
+ :policy_type => 'System Access',
+ },
+ 'Store passwords using reversible encryption' => {
+ :name => 'ClearTextPassword',
+ :policy_type => 'System Access',
+ },
+ 'Account lockout threshold' => {
+ :name => 'LockoutBadCount',
+ :policy_type => 'System Access',
+ },
+ 'Account lockout duration' => {
+ :name => 'LockoutDuration',
+ :policy_type => 'System Access',
+ },
+ 'Reset account lockout counter after' => {
+ :name => 'ResetLockoutCount',
+ :policy_type => 'System Access',
+ },
+ 'Accounts: Rename administrator account' => {
+ :name => 'NewAdministratorName',
+ :policy_type => 'System Access',
+ :data_type => :quoted_string
+ },
+ 'Accounts: Administrator account status' => {
+ :name => 'EnableAdminAccount',
+ :policy_type => 'System Access',
+ },
+ 'Accounts: Rename guest account' => {
+ :name => 'NewGuestName',
+ :policy_type => 'System Access',
+ :data_type => :quoted_string
+ },
+ 'Accounts: Require Login to Change Password' => {
+ :name => 'RequireLogonToChangePassword',
+ :policy_type => 'System Access'
+ },
+ 'Network security: Force logoff when logon hours expire' => {
+ :name => 'ForceLogoffWhenHourExpire',
+ :policy_type => 'System Access'
+ },
+ 'Network access: Allow anonymous SID/name translation' => {
+ :name => 'LSAAnonymousNameLookup',
+ :policy_type => 'System Access'
+ },
+ 'EnableAdminAccount' => {
+ :name => 'EnableAdminAccount',
+ :policy_type => 'System Access'
+ },
+ "EnableGuestAccount"=>{
+ :name=>"EnableGuestAccount",
+ :policy_type => 'System Access'
+ },
+ # Audit Policy Mappings
+ 'Audit account logon events' => {
+ :name => 'AuditAccountLogon',
+ :policy_type => 'Event Audit',
+ },
+ 'Audit account management' => {
+ :name => 'AuditAccountManage',
+ :policy_type => 'Event Audit',
+ },
+ 'Audit directory service access' => {
+ :name => 'AuditDSAccess',
+ :policy_type => 'Event Audit',
+ },
+ 'Audit logon events' => {
+ :name => 'AuditLogonEvents',
+ :policy_type => 'Event Audit',
+ },
+ 'Audit object access' => {
+ :name => 'AuditObjectAccess',
+ :policy_type => 'Event Audit',
+ },
+ 'Audit policy change' => {
+ :name => 'AuditPolicyChange',
+ :policy_type => 'Event Audit',
+ },
+ 'Audit privilege use' => {
+ :name => 'AuditPrivilegeUse',
+ :policy_type => 'Event Audit',
+ },
+ 'Audit process tracking' => {
+ :name => 'AuditProcessTracking',
+ :policy_type => 'Event Audit',
+ },
+ 'Audit system events' => {
+ :name => 'AuditSystemEvents',
+ :policy_type => 'Event Audit',
+ },
+ 'Audit: Force audit policy subcategory settings (Windows Vista or later) to override audit policy category settings' => {
+ :name => 'MACHINE\System\CurrentControlSet\Control\Lsa\SCENoApplyLegacyAuditPolicy',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ #User rights mapping
+ 'Access Credential Manager as a trusted caller' => {
+ :name => 'SeTrustedCredManAccessPrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Access this computer from the network' => {
+ :name => 'SeNetworkLogonRight',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Act as part of the operating system' => {
+ :name => 'SeTcbPrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Add workstations to domain' => {
+ :name => 'SeMachineAccountPrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Adjust memory quotas for a process' => {
+ :name => 'SeIncreaseQuotaPrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Allow log on locally' => {
+ :name => 'SeInteractiveLogonRight',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Allow log on through Remote Desktop Services' => {
+ :name => 'SeRemoteInteractiveLogonRight',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Back up files and directories' => {
+ :name => 'SeBackupPrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Bypass traverse checking' => {
+ :name => 'SeChangeNotifyPrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Change the system time' => {
+ :name => 'SeSystemtimePrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Change the time zone' => {
+ :name => 'SeTimeZonePrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Create a pagefile' => {
+ :name => 'SeCreatePagefilePrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Create a token object' => {
+ :name => 'SeCreateTokenPrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Create global objects' => {
+ :name => 'SeCreateGlobalPrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Create permanent shared objects' => {
+ :name => 'SeCreatePermanentPrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Create symbolic links' => {
+ :name => 'SeCreateSymbolicLinkPrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Debug programs' => {
+ :name => 'SeDebugPrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Deny access to this computer from the network' => {
+ :name => 'SeDenyNetworkLogonRight',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Deny log on as a batch job' => {
+ :name => 'SeDenyBatchLogonRight',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Deny log on as a service' => {
+ :name => 'SeDenyServiceLogonRight',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Deny log on locally' => {
+ :name => 'SeDenyInteractiveLogonRight',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Deny log on through Remote Desktop Services' => {
+ :name => 'SeDenyRemoteInteractiveLogonRight',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Enable computer and user accounts to be trusted for delegation' => {
+ :name => 'SeEnableDelegationPrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Force shutdown from a remote system' => {
+ :name => 'SeRemoteShutdownPrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Generate security audits' => {
+ :name => 'SeAuditPrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Impersonate a client after authentication' => {
+ :name => 'SeImpersonatePrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Increase a process working set' => {
+ :name => 'SeIncreaseWorkingSetPrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Increase scheduling priority' => {
+ :name => 'SeIncreaseBasePriorityPrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Load and unload device drivers' => {
+ :name => 'SeLoadDriverPrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Lock pages in memory' => {
+ :name => 'SeLockMemoryPrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Log on as a batch job' => {
+ :name => 'SeBatchLogonRight',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Log on as a service' => {
+ :name => 'SeServiceLogonRight',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Manage auditing and security log' => {
+ :name => 'SeSecurityPrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Modify an object label' => {
+ :name => 'SeRelabelPrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Modify firmware environment values' => {
+ :name => 'SeSystemEnvironmentPrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Perform volume maintenance tasks' => {
+ :name => 'SeManageVolumePrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Profile single process' => {
+ :name => 'SeProfileSingleProcessPrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Profile system performance' => {
+ :name => 'SeSystemProfilePrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Remove computer from docking station' => {
+ :name => 'SeUndockPrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Replace a process level token' => {
+ :name => 'SeAssignPrimaryTokenPrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Restore files and directories' => {
+ :name => 'SeRestorePrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Shut down the system' => {
+ :name => 'SeShutdownPrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Synchronize directory service data' => {
+ :name => 'SeSyncAgentPrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ 'Take ownership of files or other objects' => {
+ :name => 'SeTakeOwnershipPrivilege',
+ :policy_type => 'Privilege Rights',
+ },
+ #Registry Keys
+ 'Recovery console: Allow automatic administrative logon' => {
+ :name => 'MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Setup\RecoveryConsole\SecurityLevel',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Recovery console: Allow floppy copy and access to all drives and all folders' => {
+ :name => 'MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Setup\RecoveryConsole\SetCommand',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Interactive logon: Number of previous logons to cache (in case domain controller is not available)' => {
+ :name => 'MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\CachedLogonsCount',
+ :reg_type => '1',
+ :policy_type => 'Registry Values',
+ },
+ 'Interactive logon: Require Domain Controller authentication to unlock workstation' => {
+ :name => 'MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\ForceUnlockLogon',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Interactive logon: Prompt user to change password before expiration' => {
+ :name => 'MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\PasswordExpiryWarning',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Interactive logon: Smart card removal behavior' => {
+ :name => 'MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\ScRemoveOption',
+ :reg_type => '1',
+ :policy_type => 'Registry Values',
+ },
+ 'User Account Control: Behavior of the elevation prompt for administrators in Admin Approval Mode' => {
+ :name => 'MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ConsentPromptBehaviorAdmin',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'User Account Control: Behavior of the elevation prompt for standard users' => {
+ :name => 'MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ConsentPromptBehaviorUser',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Interactive logon: Do not require CTRL+ALT+DEL' => {
+ :name => 'MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\DisableCAD',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Interactive logon: Do not display last user name' => {
+ :name => 'MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\DontDisplayLastUserName',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'User Account Control: Detect application installations and prompt for elevation' => {
+ :name => 'MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\EnableInstallerDetection',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'User Account Control: Run all administrators in Admin Approval Mode' => {
+ :name => 'MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\EnableLUA',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'User Account Control: Only elevate UIAccess applications that are installed in secure locations' => {
+ :name => 'MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\EnableSecureUIAPaths',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'User Account Control: Allow UIAccess applications to prompt for elevation without using the secure desktop' => {
+ :name => 'MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\EnableUIADesktopToggle',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'User Account Control: Virtualize file and registry write failures to per-user locations' => {
+ :name => 'MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\EnableVirtualization',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'User Account Control: Admin Approval Mode for the Built-in Administrator account' => {
+ :name => 'MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\FilterAdministratorToken',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Interactive logon: Message title for users attempting to log on' => {
+ :name => 'MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\LegalNoticeCaption',
+ :reg_type => '1',
+ :policy_type => 'Registry Values',
+ },
+ 'Interactive logon: Message text for users attempting to log on' => {
+ :name => 'MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\LegalNoticeText',
+ :reg_type => '7',
+ :policy_type => 'Registry Values',
+ },
+ 'User Account Control: Switch to the secure desktop when prompting for elevation' => {
+ :name => 'MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\PromptOnSecureDesktop',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Interactive logon: Require smart card' => {
+ :name => 'MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ScForceOption',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Shutdown: Allow system to be shut down without having to log on' => {
+ :name => 'MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ShutdownWithoutLogon',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Devices: Allow undock without having to log on' => {
+ :name => 'MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\UndockWithoutLogon',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'User Account Control: Only elevate executables that are signed and validated' => {
+ :name => 'MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ValidateAdminCodeSignatures',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'System settings: Use Certificate Rules on Windows Executables for Software Restriction Policies' => {
+ :name => 'MACHINE\Software\Policies\Microsoft\Windows\Safer\CodeIdentifiers\AuthenticodeEnabled',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Audit: Audit the access of global system objects' => {
+ :name => 'MACHINE\System\CurrentControlSet\Control\Lsa\AuditBaseObjects',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Audit: Shut down system immediately if unable to log security audits' => {
+ :name => 'MACHINE\System\CurrentControlSet\Control\Lsa\CrashOnAuditFail',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Network access: Do not allow storage of passwords and credentials for network authentication' => {
+ :name => 'MACHINE\System\CurrentControlSet\Control\Lsa\DisableDomainCreds',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Network access: Let Everyone permissions apply to anonymous users' => {
+ :name => 'MACHINE\System\CurrentControlSet\Control\Lsa\EveryoneIncludesAnonymous',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Network access: Sharing and security model for local accounts' => {
+ :name => 'MACHINE\System\CurrentControlSet\Control\Lsa\ForceGuest',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'System cryptography: Use FIPS compliant algorithms for encryption, hashing, and signing' => {
+ :name => 'MACHINE\System\CurrentControlSet\Control\Lsa\FIPSAlgorithmPolicy\Enabled',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'System cryptography: Force strong key protection for user keys stored on the computer' => {
+ :name => 'MACHINE\Software\Policies\Microsoft\Cryptography\ForceKeyProtection',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Audit: Audit the use of Backup and Restore privilege' => {
+ :name => 'MACHINE\System\CurrentControlSet\Control\Lsa\FullPrivilegeAuditing',
+ :reg_type => '3',
+ :policy_type => 'Registry Values',
+ },
+ 'Accounts: Block Microsoft accounts' => {
+ :name => 'MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\NoConnectedUser',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Accounts: Limit local account use of blank passwords to console logon only' => {
+ :name => 'MACHINE\System\CurrentControlSet\Control\Lsa\LimitBlankPasswordUse',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Network security: All Local System to use computer identity for NTLM' => {
+ :name => 'MACHINE\System\CurrentControlSet\Control\Lsa\UseMachineId',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Network access: Remotely accessible registry paths' => {
+ :name => 'MACHINE\System\CurrentControlSet\Control\SecurePipeServers\Winreg\AllowedExactPaths\Machine',
+ :reg_type => '7',
+ :policy_type => 'Registry Values',
+ },
+ 'Devices: Restrict CD-ROM access to locally logged-on user only' => {
+ :name => 'MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\AllocateCDRoms',
+ :reg_type => '1',
+ :policy_type => 'Registry Values',
+ },
+ 'Devices: Restrict floppy access to locally logged-on user only' => {
+ :name => 'MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\AllocateFloppies',
+ :reg_type => '1',
+ :policy_type => 'Registry Values',
+ },
+ 'Devices: Allowed to format and eject removable media' => {
+ :name => 'MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\AllocateDASD',
+ :reg_type => '1',
+ :policy_type => 'Registry Values',
+ },
+ 'Devices: Prevent users from installing printer drivers' => {
+ :name => 'MACHINE\System\CurrentControlSet\Control\Print\Providers\LanMan Print Services\Servers\AddPrinterDrivers',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Domain member: Digitally encrypt or sign secure channel data (always)' => {
+ :name => 'MACHINE\System\CurrentControlSet\Services\Netlogon\Parameters\RequireSignOrSeal',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Domain member: Digitally encrypt secure channel data (when possible)' => {
+ :name => 'MACHINE\System\CurrentControlSet\Services\Netlogon\Parameters\SealSecureChannel',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Domain member: Digitally sign secure channel data (when possible)' => {
+ :name => 'MACHINE\System\CurrentControlSet\Services\Netlogon\Parameters\SignSecureChannel',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Domain member: Disable machine account password changes' => {
+ :name => 'MACHINE\System\CurrentControlSet\Services\Netlogon\Parameters\DisablePasswordChange',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Domain member: Maximum machine account password age' => {
+ :name => 'MACHINE\System\CurrentControlSet\Services\Netlogon\Parameters\MaximumPasswordAge',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Domain member: Require strong (Windows 2000 or later) session key' => {
+ :name => 'MACHINE\System\CurrentControlSet\Services\Netlogon\Parameters\RequireStrongKey',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Interactive logon: Display user information when the session is locked' => {
+ :name => 'MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\DontDisplayLockedUserId',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Interactive logon: Machine inactivity limit' => {
+ :name => 'MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\InactivityTimeoutSecs',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Interactive logon: Machine account lockout threshold' => {
+ :name => 'MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\MaxDevicePasswordFailedAttempts',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Microsoft network client: Digitally sign communications (always)' => {
+ :name => 'MACHINE\System\CurrentControlSet\Services\LanmanWorkstation\Parameters\RequireSecuritySignature',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Microsoft network client: Digitally sign communications (if server agrees)' => {
+ :name => 'MACHINE\System\CurrentControlSet\Services\LanmanWorkstation\Parameters\EnableSecuritySignature',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Microsoft network client: Send unencrypted password to third-party SMB servers' => {
+ :name => 'MACHINE\System\CurrentControlSet\Services\LanmanWorkstation\Parameters\EnablePlainTextPassword',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Microsoft network server: Server SPN target name validation level' => {
+ :name => 'MACHINE\System\CurrentControlSet\Services\LanmanServer\Parameters\SmbServerNameHardeningLevel',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Microsoft network server: Amount of idle time required before suspending session' => {
+ :name => 'MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\AutoDisconnect',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Microsoft network server: Digitally sign communications (always)' => {
+ :name => 'MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\RequireSecuritySignature',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Microsoft network server: Digitally sign communications (if client agrees)' => {
+ :name => 'MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\EnableSecuritySignature',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Microsoft network server: Disconnect clients when logon hours expire' => {
+ :name => 'MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\EnableForcedLogOff',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Network access: Named Pipes that can be accessed anonymously' => {
+ :name => 'MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\NullSessionPipes',
+ :reg_type => '7',
+ :policy_type => 'Registry Values',
+ },
+ 'Network access: Shares that can be accessed anonymously' => {
+ :name => 'MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\NullSessionShares',
+ :reg_type => '7',
+ :policy_type => 'Registry Values',
+ },
+ 'Network access: Do not allow anonymous enumeration of SAM accounts and shares' => {
+ :name => 'MACHINE\System\CurrentControlSet\Control\Lsa\RestrictAnonymous',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Network access: Do not allow anonymous enumeration of SAM accounts' => {
+ :name => 'MACHINE\System\CurrentControlSet\Control\Lsa\RestrictAnonymousSAM',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Network access: Remotely accessible registry paths and sub-paths' => {
+ :name => 'MACHINE\System\CurrentControlSet\Control\SecurePipeServers\Winreg\AllowedPaths\Machine',
+ :reg_type => '7',
+ :policy_type => 'Registry Values',
+ },
+ 'Network access: Restrict anonymous access to Named Pipes and Shares' => {
+ :name => 'MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\RestrictNullSessAccess',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Network security: Do not store LAN Manager hash value on next password change' => {
+ :name => 'MACHINE\System\CurrentControlSet\Control\Lsa\NoLMHash',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Network security: LAN Manager authentication level' => {
+ :name => 'MACHINE\System\CurrentControlSet\Control\Lsa\LmCompatibilityLevel',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Network security: Minimum session security for NTLM SSP based (including secure RPC) clients' => {
+ :name => 'MACHINE\System\CurrentControlSet\Control\Lsa\MSV1_0\NTLMMinClientSec',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Network security: Minimum session security for NTLM SSP based (including secure RPC) servers' => {
+ :name => 'MACHINE\System\CurrentControlSet\Control\Lsa\MSV1_0\NTLMMinServerSec',
+ :reg_type => '4',
+ :policy_type => 'Registry Values',
+ },
+ 'Network security: LDAP client signing requirements' => {
+ :name => 'MACHINE\System\CurrentControlSet\Services\LDAP\LDAPClientIntegrity',
+ :reg_type => "4",
+ :policy_type => "Registry Values",
+ },
+ 'System objects: Require case insensitivity for non-Windows subsystems' => {
+ :name => 'MACHINE\System\CurrentControlSet\Control\Session Manager\Kernel\ObCaseInsensitive',
+ :policy_type => "Registry Values",
+ :reg_type => "4"
+ },
+ 'System objects: Strengthen default permissions of internal system objects (e.g., Symbolic Links)' => {
+ :name => 'MACHINE\System\CurrentControlSet\Control\Session Manager\ProtectionMode',
+ :policy_type => "Registry Values",
+ :reg_type => "4"
+ },
+ 'System settings: Optional subsystems' => {
+ :name => 'MACHINE\System\CurrentControlSet\Control\Session Manager\SubSystems\optional',
+ :policy_type => "Registry Values",
+ :reg_type => "7"
+ },
+ 'Shutdown: Clear virtual memory pagefile' => {
+ :name => 'MACHINE\System\CurrentControlSet\Control\Session Manager\Memory Management\ClearPageFileAtShutdown',
+ :policy_type => "Registry Values",
+ :reg_type => "4"
+ },
+
+ }
+ end
+end
diff --git a/modules/utilities/windows/system/local_security_policy/lib/puppet_x/twp/inifile.rb b/modules/utilities/windows/system/local_security_policy/lib/puppet_x/twp/inifile.rb
new file mode 100644
index 000000000..a43030bde
--- /dev/null
+++ b/modules/utilities/windows/system/local_security_policy/lib/puppet_x/twp/inifile.rb
@@ -0,0 +1,629 @@
+#encoding: UTF-8
+
+# This class represents the INI file and can be used to parse, modify,
+# and write INI files.
+module PuppetX
+class IniFile
+ include Enumerable
+
+ class Error < StandardError; end
+ VERSION = '3.0.0'
+
+ # Public: Open an INI file and load the contents.
+ #
+ # filename - The name of the file as a String
+ # opts - The Hash of options (default: {})
+ # :comment - String containing the comment character(s)
+ # :parameter - String used to separate parameter and value
+ # :encoding - Encoding String for reading / writing
+ # :default - The String name of the default global section
+ #
+ # Examples
+ #
+ # IniFile.load('file.ini')
+ # #=> IniFile instance
+ #
+ # IniFile.load('does/not/exist.ini')
+ # #=> nil
+ #
+ # Returns an IniFile instance or nil if the file could not be opened.
+ def self.load( filename, opts = {} )
+ return unless File.file? filename
+ new(opts.merge(:filename => filename))
+ end
+
+ # Get and set the filename
+ attr_accessor :filename
+
+ # Get and set the encoding
+ attr_accessor :encoding
+
+ # Public: Create a new INI file from the given set of options. If :content
+ # is provided then it will be used to populate the INI file. If a :filename
+ # is provided then the contents of the file will be parsed and stored in the
+ # INI file. If neither the :content or :filename is provided then an empty
+ # INI file is created.
+ #
+ # opts - The Hash of options (default: {})
+ # :content - The String/Hash containing the INI contents
+ # :comment - String containing the comment character(s)
+ # :parameter - String used to separate parameter and value
+ # :encoding - Encoding String for reading / writing
+ # :default - The String name of the default global section
+ # :filename - The filename as a String
+ #
+ # Examples
+ #
+ # IniFile.new
+ # #=> an empty IniFile instance
+ #
+ # IniFile.new( :content => "[global]\nfoo=bar" )
+ # #=> an IniFile instance
+ #
+ # IniFile.new( :filename => 'file.ini', :encoding => 'UTF-8' )
+ # #=> an IniFile instance
+ #
+ # IniFile.new( :content => "[global]\nfoo=bar", :comment => '#' )
+ # #=> an IniFile instance
+ #
+ def initialize( opts = {} )
+ @comment = opts.fetch(:comment, ';#')
+ @param = opts.fetch(:parameter, '=')
+ @encoding = opts.fetch(:encoding, nil)
+ @default = opts.fetch(:default, 'global')
+ @filename = opts.fetch(:filename, nil)
+ content = opts.fetch(:content, nil)
+
+ @ini = Hash.new {|h,k| h[k] = Hash.new}
+
+ if content.is_a?(Hash) then merge!(content)
+ elsif content then parse(content)
+ elsif @filename then read
+ end
+ end
+
+ # Public: Write the contents of this IniFile to the file system. If left
+ # unspecified, the currently configured filename and encoding will be used.
+ # Otherwise the filename and encoding can be specified in the options hash.
+ #
+ # opts - The default options Hash
+ # :filename - The filename as a String
+ # :encoding - The encoding as a String
+ #
+ # Returns this IniFile instance.
+ def write( opts = {} )
+ filename = opts.fetch(:filename, @filename)
+ encoding = opts.fetch(:encoding, @encoding)
+ mode = encoding ? "w:#{encoding}" : "w"
+
+ File.open(filename, mode) do |f|
+ @ini.each do |section,hash|
+ f.puts "[#{section}]"
+ hash.each {|param,val| f.puts "#{param} #{@param} #{escape_value val}"}
+ f.puts
+ end
+ end
+
+ self
+ end
+ alias :save :write
+
+ # Public: Read the contents of the INI file from the file system and replace
+ # and set the state of this IniFile instance. If left unspecified the
+ # currently configured filename and encoding will be used when reading from
+ # the file system. Otherwise the filename and encoding can be specified in
+ # the options hash.
+ #
+ # opts - The default options Hash
+ # :filename - The filename as a String
+ # :encoding - The encoding as a String
+ #
+ # Returns this IniFile instance if the read was successful; nil is returned
+ # if the file could not be read.
+ def read( opts = {} )
+ filename = opts.fetch(:filename, @filename)
+ encoding = opts.fetch(:encoding, @encoding)
+ return unless File.file? filename
+ mode = encoding ? "r:#{encoding}" : "r"
+
+ File.open(filename, mode) { |fd| parse fd }
+ self
+ end
+ alias :restore :read
+
+ # Returns this IniFile converted to a String.
+ def to_s
+ s = []
+ @ini.each do |section,hash|
+ s << "[#{section}]"
+ hash.each {|param,val| s << "#{param} #{@param} #{escape_value val}"}
+ s << ""
+ end
+ s.join("\n")
+ end
+
+ # Returns this IniFile converted to a Hash.
+ def to_h
+ @ini.dup
+ end
+
+ # Public: Creates a copy of this inifile with the entries from the
+ # other_inifile merged into the copy.
+ #
+ # other - The other IniFile.
+ #
+ # Returns a new IniFile.
+ def merge( other )
+ self.dup.merge!(other)
+ end
+
+ # Public: Merges other_inifile into this inifile, overwriting existing
+ # entries. Useful for having a system inifile with user overridable settings
+ # elsewhere.
+ #
+ # other - The other IniFile.
+ #
+ # Returns this IniFile.
+ def merge!( other )
+ return self if other.nil?
+
+ my_keys = @ini.keys
+ other_keys = case other
+ when IniFile
+ other.instance_variable_get(:@ini).keys
+ when Hash
+ other.keys
+ else
+ raise Error, "cannot merge contents from '#{other.class.name}'"
+ end
+
+ (my_keys & other_keys).each do |key|
+ case other[key]
+ when Hash
+ @ini[key].merge!(other[key])
+ when nil
+ nil
+ else
+ raise Error, "cannot merge section #{key.inspect} - unsupported type: #{other[key].class.name}"
+ end
+ end
+
+ (other_keys - my_keys).each do |key|
+ @ini[key] = case other[key]
+ when Hash
+ other[key].dup
+ when nil
+ {}
+ else
+ raise Error, "cannot merge section #{key.inspect} - unsupported type: #{other[key].class.name}"
+ end
+ end
+
+ self
+ end
+
+ # Public: Yield each INI file section, parameter, and value in turn to the
+ # given block.
+ #
+ # block - The block that will be iterated by the each method. The block will
+ # be passed the current section and the parameter/value pair.
+ #
+ # Examples
+ #
+ # inifile.each do |section, parameter, value|
+ # puts "#{parameter} = #{value} [in section - #{section}]"
+ # end
+ #
+ # Returns this IniFile.
+ def each
+ return unless block_given?
+ @ini.each do |section,hash|
+ hash.each do |param,val|
+ yield section, param, val
+ end
+ end
+ self
+ end
+
+ # Public: Yield each section in turn to the given block.
+ #
+ # block - The block that will be iterated by the each method. The block will
+ # be passed the current section as a Hash.
+ #
+ # Examples
+ #
+ # inifile.each_section do |section|
+ # puts section.inspect
+ # end
+ #
+ # Returns this IniFile.
+ def each_section
+ return unless block_given?
+ @ini.each_key {|section| yield section}
+ self
+ end
+
+ # Public: Remove a section identified by name from the IniFile.
+ #
+ # section - The section name as a String.
+ #
+ # Returns the deleted section Hash.
+ def delete_section( section )
+ @ini.delete section.to_s
+ end
+
+ # Public: Get the section Hash by name. If the section does not exist, then
+ # it will be created.
+ #
+ # section - The section name as a String.
+ #
+ # Examples
+ #
+ # inifile['global']
+ # #=> global section Hash
+ #
+ # Returns the Hash of parameter/value pairs for this section.
+ def []( section )
+ return nil if section.nil?
+ @ini[section.to_s]
+ end
+
+ # Public: Set the section to a hash of parameter/value pairs.
+ #
+ # section - The section name as a String.
+ # value - The Hash of parameter/value pairs.
+ #
+ # Examples
+ #
+ # inifile['tenderloin'] = { 'gritty' => 'yes' }
+ # #=> { 'gritty' => 'yes' }
+ #
+ # Returns the value Hash.
+ def []=( section, value )
+ @ini[section.to_s] = value
+ end
+
+ # Public: Create a Hash containing only those INI file sections whose names
+ # match the given regular expression.
+ #
+ # regex - The Regexp used to match section names.
+ #
+ # Examples
+ #
+ # inifile.match(/^tree_/)
+ # #=> Hash of matching sections
+ #
+ # Return a Hash containing only those sections that match the given regular
+ # expression.
+ def match( regex )
+ @ini.dup.delete_if { |section, _| section !~ regex }
+ end
+
+ # Public: Check to see if the IniFile contains the section.
+ #
+ # section - The section name as a String.
+ #
+ # Returns true if the section exists in the IniFile.
+ def has_section?( section )
+ @ini.has_key? section.to_s
+ end
+
+ # Returns an Array of section names contained in this IniFile.
+ def sections
+ @ini.keys
+ end
+
+ # Public: Freeze the state of this IniFile object. Any attempts to change
+ # the object will raise an error.
+ #
+ # Returns this IniFile.
+ def freeze
+ super
+ @ini.each_value {|h| h.freeze}
+ @ini.freeze
+ self
+ end
+
+ # Public: Mark this IniFile as tainted -- this will traverse each section
+ # marking each as tainted.
+ #
+ # Returns this IniFile.
+ def taint
+ super
+ @ini.each_value {|h| h.taint}
+ @ini.taint
+ self
+ end
+
+ # Public: Produces a duplicate of this IniFile. The duplicate is independent
+ # of the original -- i.e. the duplicate can be modified without changing the
+ # original. The tainted state of the original is copied to the duplicate.
+ #
+ # Returns a new IniFile.
+ def dup
+ other = super
+ other.instance_variable_set(:@ini, Hash.new {|h,k| h[k] = Hash.new})
+ @ini.each_pair {|s,h| other[s].merge! h}
+ other.taint if self.tainted?
+ other
+ end
+
+ # Public: Produces a duplicate of this IniFile. The duplicate is independent
+ # of the original -- i.e. the duplicate can be modified without changing the
+ # original. The tainted state and the frozen state of the original is copied
+ # to the duplicate.
+ #
+ # Returns a new IniFile.
+ def clone
+ other = dup
+ other.freeze if self.frozen?
+ other
+ end
+
+ # Public: Compare this IniFile to some other IniFile. For two INI files to
+ # be equivalent, they must have the same sections with the same parameter /
+ # value pairs in each section.
+ #
+ # other - The other IniFile.
+ #
+ # Returns true if the INI files are equivalent and false if they differ.
+ def eql?( other )
+ return true if equal? other
+ return false unless other.instance_of? self.class
+ @ini == other.instance_variable_get(:@ini)
+ end
+ alias :== :eql?
+
+ # Escape special characters.
+ #
+ # value - The String value to escape.
+ #
+ # Returns the escaped value.
+ def escape_value( value )
+ value = value.to_s.dup
+ value.gsub!(%r/\\([0nrt])/, '\\\\\1')
+ value.gsub!(%r/\n/, '\n')
+ value.gsub!(%r/\r/, '\r')
+ value.gsub!(%r/\t/, '\t')
+ value.gsub!(%r/\0/, '\0')
+ value
+ end
+
+ # Parse the given content and store the information in this IniFile
+ # instance. All data will be cleared out and replaced with the information
+ # read from the content.
+ #
+ # content - A String or a file descriptor (must respond to `each_line`)
+ #
+ # Returns this IniFile.
+ def parse( content )
+ parser = Parser.new(@ini, @param, @comment, @default)
+ parser.parse(content)
+ self
+ end
+
+ # The IniFile::Parser has the responsibility of reading the contents of an
+ # .ini file and storing that information into a ruby Hash. The object being
+ # parsed must respond to `each_line` - this includes Strings and any IO
+ # object.
+ class Parser
+
+ attr_writer :section
+ attr_accessor :property
+ attr_accessor :value
+
+ # Create a new IniFile::Parser that can be used to parse the contents of
+ # an .ini file.
+ #
+ # hash - The Hash where parsed information will be stored
+ # param - String used to separate parameter and value
+ # comment - String containing the comment character(s)
+ # default - The String name of the default global section
+ #
+ def initialize( hash, param, comment, default )
+ @hash = hash
+ @default = default
+
+ comment = comment.to_s.empty? ? "\\z" : "\\s*(?:[#{comment}].*)?\\z"
+
+ @section_regexp = %r/\A\s*\[([^\]]+)\]#{comment}/
+ @ignore_regexp = %r/\A#{comment}/
+ @property_regexp = %r/\A(.*?)(? true
+ # "false" --> false
+ # "" --> nil
+ # "42" --> 42
+ # "3.14" --> 3.14
+ # "foo" --> "foo"
+ #
+ # Returns the typecast value.
+ def typecast( value )
+ case value
+ when %r/\Atrue\z/i; true
+ when %r/\Afalse\z/i; false
+ when %r/\A\s*\z/i; nil
+ else
+ Integer(value) rescue \
+ Float(value) rescue \
+ unescape_value(value)
+ end
+ end
+
+ # Unescape special characters found in the value string. This will convert
+ # escaped null, tab, carriage return, newline, and backslash into their
+ # literal equivalents.
+ #
+ # value - The String value to unescape.
+ #
+ # Returns the unescaped value.
+ def unescape_value( value )
+ value = value.to_s
+ value.gsub!(%r/\\[0nrt\\]/) { |char|
+ case char
+ when '\0'; "\0"
+ when '\n'; "\n"
+ when '\r'; "\r"
+ when '\t'; "\t"
+ when '\\\\'; "\\"
+ end
+ }
+ value
+ end
+ end
+
+end # IniFile
+end
diff --git a/modules/utilities/windows/system/local_security_policy/local_security_policy.pp b/modules/utilities/windows/system/local_security_policy/local_security_policy.pp
new file mode 100644
index 000000000..8d1c8b69c
--- /dev/null
+++ b/modules/utilities/windows/system/local_security_policy/local_security_policy.pp
@@ -0,0 +1 @@
+
diff --git a/modules/utilities/windows/system/local_security_policy/manifests/init.pp b/modules/utilities/windows/system/local_security_policy/manifests/init.pp
new file mode 100644
index 000000000..c39161a7e
--- /dev/null
+++ b/modules/utilities/windows/system/local_security_policy/manifests/init.pp
@@ -0,0 +1,14 @@
+# Class: local_security_policy
+#
+# This module manages local_security_policy
+#
+# Parameters: none
+#
+# Actions:
+#
+# Requires: see Modulefile
+#
+# Sample Usage:
+#
+class local_security_policy {
+}
diff --git a/modules/utilities/windows/system/local_security_policy/metadata.json b/modules/utilities/windows/system/local_security_policy/metadata.json
new file mode 100644
index 000000000..ce885afdd
--- /dev/null
+++ b/modules/utilities/windows/system/local_security_policy/metadata.json
@@ -0,0 +1,23 @@
+{
+ "name": "ayohrling-local_security_policy",
+ "version": "0.6.3",
+ "author": "Paul S. Cannon, Adam Yohrling, Corey Osman, Ryan Russell-Yates, Jordan Wesolowski, Gerben Welter, Thomas Linkin",
+ "summary": "Windows Local Security Policy management. Forked from cannonps/local_security_policy",
+ "license": "Apache 2.0",
+ "source": "https://github.com/ayohrling/local_security_policy",
+ "project_page": "https://github.com/ayohrling/local_security_policy",
+ "issues_url": "https://github.com/ayohrling/local_security_policy/issues",
+ "dependencies": [
+
+ ],
+ "data_provider": null,
+ "tags": [
+ "windows",
+ "security"
+ ],
+ "operatingsystem_support": [
+ {
+ "operatingsystem": "windows"
+ }
+ ]
+}
diff --git a/modules/utilities/windows/system/local_security_policy/secgen_metadata.xml b/modules/utilities/windows/system/local_security_policy/secgen_metadata.xml
new file mode 100644
index 000000000..df79bb017
--- /dev/null
+++ b/modules/utilities/windows/system/local_security_policy/secgen_metadata.xml
@@ -0,0 +1,26 @@
+
+
+
+ Local Security Policy
+ Paul Cannon
+ Adam Yohrling
+ Emlyn Butterfield
+ Apache v2
+ Configure local security policy (LSP) for windows servers.
+
+
+ system
+ windows
+
+
+ https://forge.puppet.com/ayohrling/local_security_policy
+
+ accounts
+
+
+ bases/(debian|kali|ubuntu).*
+
+
+
diff --git a/modules/utilities/windows/system/local_security_policy/spec/classes/local_security_policy_spec.rb b/modules/utilities/windows/system/local_security_policy/spec/classes/local_security_policy_spec.rb
new file mode 100644
index 000000000..bc991bd83
--- /dev/null
+++ b/modules/utilities/windows/system/local_security_policy/spec/classes/local_security_policy_spec.rb
@@ -0,0 +1,29 @@
+require 'spec_helper'
+require 'shared_contexts'
+
+describe 'local_security_policy' do
+ # by default the hiera integration uses hiera data from the shared_contexts.rb file
+ # but basically to mock hiera you first need to add a key/value pair
+ # to the specific context in the spec/shared_contexts.rb file
+ # Note: you can only use a single hiera context per describe/context block
+ # rspec-puppet does not allow you to swap out hiera data on a per test block
+ #include_context :hiera
+
+
+ # below is the facts hash that gives you the ability to mock
+ # facts on a per describe/context block. If you use a fact in your
+ # manifest you should mock the facts below.
+ let(:facts) do
+ {}
+ end
+ # below is a list of the resource parameters that you can override.
+ # By default all non-required parameters are commented out,
+ # while all required parameters will require you to add a value
+ let(:params) do
+ {
+ }
+ end
+ # add these two lines in a single test block to enable puppet and hiera debug mode
+ # Puppet::Util::Log.level = :debug
+ # Puppet::Util::Log.newdestination(:console)
+end
diff --git a/modules/utilities/windows/system/local_security_policy/spec/fixtures/unit/group.txt b/modules/utilities/windows/system/local_security_policy/spec/fixtures/unit/group.txt
new file mode 100755
index 000000000..a228f9f17
--- /dev/null
+++ b/modules/utilities/windows/system/local_security_policy/spec/fixtures/unit/group.txt
@@ -0,0 +1,18 @@
+
+Node,Name,SID
+win2008r2,Administrators,S-1-5-32-544
+win2008r2,Backup Operators,S-1-5-32-551
+win2008r2,Certificate Service DCOM Access,S-1-5-32-574
+win2008r2,Cryptographic Operators,S-1-5-32-569
+win2008r2,Distributed COM Users,S-1-5-32-562
+win2008r2,Event Log Readers,S-1-5-32-573
+win2008r2,Guests,S-1-5-32-546
+win2008r2,IIS_IUSRS,S-1-5-32-568
+win2008r2,Network Configuration Operators,S-1-5-32-556
+win2008r2,Performance Log Users,S-1-5-32-559
+win2008r2,Performance Monitor Users,S-1-5-32-558
+win2008r2,Power Users,S-1-5-32-547
+win2008r2,Print Operators,S-1-5-32-550
+win2008r2,Remote Desktop Users,S-1-5-32-555
+win2008r2,Replicator,S-1-5-32-552
+win2008r2,Users,S-1-5-32-545
\ No newline at end of file
diff --git a/modules/utilities/windows/system/local_security_policy/spec/fixtures/unit/secedit.inf b/modules/utilities/windows/system/local_security_policy/spec/fixtures/unit/secedit.inf
new file mode 100644
index 000000000..d7623bd2b
--- /dev/null
+++ b/modules/utilities/windows/system/local_security_policy/spec/fixtures/unit/secedit.inf
@@ -0,0 +1,124 @@
+[Unicode]
+Unicode=yes
+[System Access]
+MinimumPasswordAge = 1
+MaximumPasswordAge = 60
+MinimumPasswordLength = 14
+PasswordComplexity = 1
+PasswordHistorySize = 24
+LockoutBadCount = 5
+ResetLockoutCount = 15
+LockoutDuration = 15
+RequireLogonToChangePassword = 0
+ForceLogoffWhenHourExpire = 0
+NewAdministratorName = "Executive"
+NewGuestName = "Unwanted"
+ClearTextPassword = 0
+LSAAnonymousNameLookup = 0
+EnableAdminAccount = 1
+EnableGuestAccount = 0
+[Event Audit]
+AuditSystemEvents = 0
+AuditLogonEvents = 0
+AuditObjectAccess = 0
+AuditPrivilegeUse = 0
+AuditPolicyChange = 0
+AuditAccountManage = 0
+AuditProcessTracking = 0
+AuditDSAccess = 0
+AuditAccountLogon = 0
+[Version]
+signature="$CHICAGO$"
+Revision=1
+[Registry Values]
+MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Setup\RecoveryConsole\SecurityLevel=4,0
+MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Setup\RecoveryConsole\SetCommand=4,0
+MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\CachedLogonsCount=1,"4"
+MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\ForceUnlockLogon=4,0
+MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\PasswordExpiryWarning=4,14
+MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\ScRemoveOption=1,"1"
+MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ConsentPromptBehaviorAdmin=4,5
+MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ConsentPromptBehaviorUser=4,3
+MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\DisableCAD=4,0
+MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\DontDisplayLastUserName=4,1
+MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\EnableInstallerDetection=4,1
+MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\EnableLUA=4,1
+MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\EnableSecureUIAPaths=4,1
+MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\EnableUIADesktopToggle=4,0
+MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\EnableVirtualization=4,1
+MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\FilterAdministratorToken=4,1
+MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\LegalNoticeCaption=1,"Warning!"
+MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\LegalNoticeText=7,WARNING! If you are not authorized to use this private network"," please disconnect immediately. Unauthorized access is prohibited and may result in civil and/or criminal prosecution. Users expressly consent to having their activities monitored"," recorded"," and shared with third parties. By your continued use of this network"," you acknowledge that you have read"," understood"," and agree with this warning message.
+MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\PromptOnSecureDesktop=4,1
+MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ScForceOption=4,0
+MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ShutdownWithoutLogon=4,0
+MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\UndockWithoutLogon=4,0
+MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ValidateAdminCodeSignatures=4,0
+MACHINE\Software\Policies\Microsoft\Windows\Safer\CodeIdentifiers\AuthenticodeEnabled=4,0
+MACHINE\System\CurrentControlSet\Control\Lsa\AuditBaseObjects=4,0
+MACHINE\System\CurrentControlSet\Control\Lsa\CrashOnAuditFail=4,0
+MACHINE\System\CurrentControlSet\Control\Lsa\DisableDomainCreds=4,0
+MACHINE\System\CurrentControlSet\Control\Lsa\EveryoneIncludesAnonymous=4,0
+MACHINE\System\CurrentControlSet\Control\Lsa\FIPSAlgorithmPolicy\Enabled=4,1
+MACHINE\System\CurrentControlSet\Control\Lsa\ForceGuest=4,0
+MACHINE\System\CurrentControlSet\Control\Lsa\FullPrivilegeAuditing=3,0
+MACHINE\System\CurrentControlSet\Control\Lsa\LimitBlankPasswordUse=4,1
+MACHINE\System\CurrentControlSet\Control\Lsa\MSV1_0\NTLMMinClientSec=4,536870912
+MACHINE\System\CurrentControlSet\Control\Lsa\MSV1_0\NTLMMinServerSec=4,536870912
+MACHINE\System\CurrentControlSet\Control\Lsa\NoLMHash=4,1
+MACHINE\System\CurrentControlSet\Control\Lsa\RestrictAnonymous=4,0
+MACHINE\System\CurrentControlSet\Control\Lsa\RestrictAnonymousSAM=4,1
+MACHINE\System\CurrentControlSet\Control\Print\Providers\LanMan Print Services\Servers\AddPrinterDrivers=4,1
+MACHINE\System\CurrentControlSet\Control\SecurePipeServers\Winreg\AllowedExactPaths\Machine=7,System\CurrentControlSet\Control\ProductOptions,System\CurrentControlSet\Control\Server Applications,Software\Microsoft\Windows NT\CurrentVersion
+MACHINE\System\CurrentControlSet\Control\SecurePipeServers\Winreg\AllowedPaths\Machine=7,System\CurrentControlSet\Control\Print\Printers,System\CurrentControlSet\Services\Eventlog,Software\Microsoft\OLAP Server,Software\Microsoft\Windows NT\CurrentVersion\Print,Software\Microsoft\Windows NT\CurrentVersion\Windows,System\CurrentControlSet\Control\ContentIndex,System\CurrentControlSet\Control\Terminal Server,System\CurrentControlSet\Control\Terminal Server\UserConfig,System\CurrentControlSet\Control\Terminal Server\DefaultUserConfiguration,Software\Microsoft\Windows NT\CurrentVersion\Perflib,System\CurrentControlSet\Services\SysmonLog
+MACHINE\System\CurrentControlSet\Control\Session Manager\Kernel\ObCaseInsensitive=4,1
+MACHINE\System\CurrentControlSet\Control\Session Manager\Memory Management\ClearPageFileAtShutdown=4,0
+MACHINE\System\CurrentControlSet\Control\Session Manager\ProtectionMode=4,1
+MACHINE\System\CurrentControlSet\Control\Session Manager\SubSystems\optional=7,Posix
+MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\AutoDisconnect=4,15
+MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\EnableForcedLogOff=4,1
+MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\EnableSecuritySignature=4,0
+MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\NullSessionPipes=7,
+MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\RequireSecuritySignature=4,0
+MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\RestrictNullSessAccess=4,1
+MACHINE\System\CurrentControlSet\Services\LanmanWorkstation\Parameters\EnablePlainTextPassword=4,0
+MACHINE\System\CurrentControlSet\Services\LanmanWorkstation\Parameters\EnableSecuritySignature=4,1
+MACHINE\System\CurrentControlSet\Services\LanmanWorkstation\Parameters\RequireSecuritySignature=4,0
+MACHINE\System\CurrentControlSet\Services\LDAP\LDAPClientIntegrity=4,1
+MACHINE\System\CurrentControlSet\Services\Netlogon\Parameters\DisablePasswordChange=4,0
+MACHINE\System\CurrentControlSet\Services\Netlogon\Parameters\MaximumPasswordAge=4,30
+MACHINE\System\CurrentControlSet\Services\Netlogon\Parameters\RequireSignOrSeal=4,1
+MACHINE\System\CurrentControlSet\Services\Netlogon\Parameters\RequireStrongKey=4,1
+MACHINE\System\CurrentControlSet\Services\Netlogon\Parameters\SealSecureChannel=4,1
+MACHINE\System\CurrentControlSet\Services\Netlogon\Parameters\SignSecureChannel=4,1
+[Privilege Rights]
+SeNetworkLogonRight = *S-1-5-11,*S-1-5-32-544
+SeBackupPrivilege = *S-1-5-32-544
+SeChangeNotifyPrivilege = *S-1-5-11,*S-1-5-19,*S-1-5-20,*S-1-5-32-544,*S-1-5-32-551
+SeSystemtimePrivilege = *S-1-5-19,*S-1-5-32-544
+SeCreatePagefilePrivilege = *S-1-5-32-544
+SeDebugPrivilege = *S-1-5-32-544
+SeRemoteShutdownPrivilege = *S-1-5-32-544
+SeAuditPrivilege = *S-1-5-19,*S-1-5-20
+SeIncreaseQuotaPrivilege = *S-1-5-19,*S-1-5-20,*S-1-5-32-544
+SeIncreaseBasePriorityPrivilege = *S-1-5-32-544
+SeLoadDriverPrivilege = *S-1-5-32-544
+SeBatchLogonRight = *S-1-5-32-544,*S-1-5-32-551,*S-1-5-32-559
+SeServiceLogonRight = *S-1-5-80-0
+SeInteractiveLogonRight = *S-1-5-32-544
+SeSecurityPrivilege = *S-1-5-32-544
+SeSystemEnvironmentPrivilege = *S-1-5-32-544
+SeProfileSingleProcessPrivilege = *S-1-5-32-544
+SeSystemProfilePrivilege = *S-1-5-32-544,*S-1-5-80-3139157870-2983391045-3678747466-658725712-1809340420
+SeAssignPrimaryTokenPrivilege = *S-1-5-19,*S-1-5-20
+SeRestorePrivilege = *S-1-5-32-544
+SeShutdownPrivilege = *S-1-5-32-544
+SeTakeOwnershipPrivilege = *S-1-5-32-544
+SeUndockPrivilege = *S-1-5-32-544
+SeManageVolumePrivilege = *S-1-5-32-544
+SeRemoteInteractiveLogonRight = *S-1-5-32-544,*S-1-5-32-555
+SeImpersonatePrivilege = *S-1-5-19,*S-1-5-20,*S-1-5-32-544,*S-1-5-6
+SeCreateGlobalPrivilege = *S-1-5-19,*S-1-5-20,*S-1-5-32-544,*S-1-5-6
+SeIncreaseWorkingSetPrivilege = *S-1-5-19,*S-1-5-32-544
+SeTimeZonePrivilege = *S-1-5-19,*S-1-5-32-544
+SeCreateSymbolicLinkPrivilege = *S-1-5-32-544
diff --git a/modules/utilities/windows/system/local_security_policy/spec/fixtures/unit/short_secedit.inf b/modules/utilities/windows/system/local_security_policy/spec/fixtures/unit/short_secedit.inf
new file mode 100644
index 000000000..a776edd19
--- /dev/null
+++ b/modules/utilities/windows/system/local_security_policy/spec/fixtures/unit/short_secedit.inf
@@ -0,0 +1,14 @@
+[Unicode]
+Unicode=yes
+[System Access]
+MinimumPasswordAge = 1
+[Event Audit]
+AuditSystemEvents = 0
+[Version]
+signature="$CHICAGO$"
+Revision=1
+[Registry Values]
+MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\CachedLogonsCount=1,"4"
+MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Setup\RecoveryConsole\SecurityLevel=4,0
+[Privilege Rights]
+SeNetworkLogonRight = *S-1-5-11,*S-1-5-32-544
\ No newline at end of file
diff --git a/modules/utilities/windows/system/local_security_policy/spec/fixtures/unit/useraccount.txt b/modules/utilities/windows/system/local_security_policy/spec/fixtures/unit/useraccount.txt
new file mode 100755
index 000000000..89a9a3837
--- /dev/null
+++ b/modules/utilities/windows/system/local_security_policy/spec/fixtures/unit/useraccount.txt
@@ -0,0 +1,5 @@
+
+Node,Name,SID
+win2008r2,Executive,S-1-5-21-3856814960-2022588966-2476110078-500
+win2008r2,Unwanted,S-1-5-21-3856814960-2022588966-2476110078-501
+win2008r2,vagrant,S-1-5-21-3856814960-2022588966-2476110078-1000
\ No newline at end of file
diff --git a/modules/utilities/windows/system/local_security_policy/spec/shared_contexts.rb b/modules/utilities/windows/system/local_security_policy/spec/shared_contexts.rb
new file mode 100644
index 000000000..66c40a505
--- /dev/null
+++ b/modules/utilities/windows/system/local_security_policy/spec/shared_contexts.rb
@@ -0,0 +1,35 @@
+# optional, this should be the path to where the hiera data config file is in this repo
+# You must update this if your actual hiera data lives inside your module.
+# I only assume its data, but it could be anything
+hiera_config_file = File.expand_path(File.join(File.dirname(__FILE__), '..','data', 'hiera.yaml'))
+
+# hiera_config and hiera_data are mutually exclusive contexts.
+
+shared_context :hiera do
+ # example only,
+ let(:hiera_data) do
+ {:some_key => "some_value" }
+ end
+end
+
+shared_context :linux_hiera do
+ # example only,
+ let(:hiera_data) do
+ {:some_key => "some_value" }
+ end
+end
+
+# In case you want a more specific set of mocked hiera data for windows
+shared_context :windows_hiera do
+ # example only,
+ let(:hiera_data) do
+ {:some_key => "some_value" }
+ end
+end
+
+# you cannot use this in addition to any of the hiera_data contexts above
+shared_context :real_hiera_data do
+ let(:hiera_config) do
+ hirea_config_file
+ end
+end
diff --git a/modules/utilities/windows/system/local_security_policy/spec/spec_helper.rb b/modules/utilities/windows/system/local_security_policy/spec/spec_helper.rb
new file mode 100644
index 000000000..54aec4df0
--- /dev/null
+++ b/modules/utilities/windows/system/local_security_policy/spec/spec_helper.rb
@@ -0,0 +1,16 @@
+require 'puppetlabs_spec_helper/module_spec_helper'
+require 'rspec-puppet-utils'
+
+def fixtures_path
+ proj_root = File.expand_path(File.join(File.dirname(__FILE__), '..'))
+ fixtures_path = File.join(proj_root, 'spec', 'fixtures')
+end
+
+
+# Uncomment this to show coverage report, also useful for debugging
+#at_exit { RSpec::Puppet::Coverage.report! }
+
+RSpec.configure do |c|
+ c.formatter = 'documentation'
+ c.mock_with :rspec
+end
diff --git a/modules/utilities/windows/system/local_security_policy/spec/unit/puppet/provider/local_security_policy/policy_spec.rb b/modules/utilities/windows/system/local_security_policy/spec/unit/puppet/provider/local_security_policy/policy_spec.rb
new file mode 100644
index 000000000..67c161232
--- /dev/null
+++ b/modules/utilities/windows/system/local_security_policy/spec/unit/puppet/provider/local_security_policy/policy_spec.rb
@@ -0,0 +1,174 @@
+require 'spec_helper'
+require "awesome_print"
+
+provider_class = Puppet::Type.type(:local_security_policy).provider(:policy)
+
+describe provider_class do
+ subject { provider_class }
+ before :all do
+ Puppet::Util.stubs(:which).with("wmic").returns("c:\\tools\\wmic")
+ Puppet::Util.stubs(:which).with("secedit").returns("c:\\tools\\secedit")
+ end
+ before :each do
+ infout = StringIO.new
+ sdbout = StringIO.new
+ allow(provider_class).to receive(:read_policy_settings).and_return(inf_data)
+ allow(Tempfile).to receive(:new).with('infimport').and_return(infout)
+ allow(Tempfile).to receive(:new).with('sdbimport').and_return(sdbout)
+ allow(File).to receive(:file?).with(secdata).and_return(true)
+ # the below mock seems to be required or rspec complains
+ allow(File).to receive(:file?).with(/facter/).and_return(true)
+ allow(subject).to receive(:temp_file).and_return(secdata)
+ subject.stubs(:secedit).with(['/configure', '/db', 'sdbout', '/cfg', 'infout', '/quiet'])
+ subject.stubs(:secedit).with(['/export', '/cfg', secdata, '/quiet'])
+ allow_any_instance_of(SecurityPolicy).to receive(:wmic).with([ "useraccount", "get", "name,sid", "/format:csv"]).and_return(userdata)
+ allow_any_instance_of(SecurityPolicy).to receive(:wmic).with([ "group", "get", "name,sid", "/format:csv"]).and_return(groupdata)
+
+ end
+
+ let(:security_policy){
+ SecurityPolicy.new
+ }
+
+ let(:inf_data) do
+ inffile_content = File.read(secdata).encode('utf-8', :universal_newline => true).gsub("\xEF\xBB\xBF", '')
+ PuppetX::IniFile.new(:content => inffile_content)
+ end
+ # mock up the data which was gathered on a real windows system
+ let(:secdata) do
+ File.join(fixtures_path, 'unit', 'secedit.inf')
+ end
+
+ let(:groupdata) do
+ file = File.join(fixtures_path, 'unit', 'group.txt')
+ File.open(file, 'r') {|f| f.read.encode('utf-8', :universal_newline => true).gsub("\xEF\xBB\xBF", '')}
+ end
+
+ let(:userdata) do
+ file = File.join(fixtures_path, 'unit', 'useraccount.txt')
+ File.open(file, 'r') {|f| f.read.encode('utf-8', :universal_newline => true).gsub("\xEF\xBB\xBF", '')}
+ end
+
+ let(:facts)do {:is_virtual => 'false', :operatingsystem => 'windows'} end
+
+ let(:resource) {
+ Puppet::Type.type(:local_security_policy).new(
+ :name => 'Network access: Let Everyone permissions apply to anonymous users',
+ :ensure => 'present',
+ :policy_setting => 'MACHINE\System\CurrentControlSet\Control\Lsa\EveryoneIncludesAnonymous',
+ :policy_type => 'Registry Values',
+ :policy_value => '0')
+ }
+ let(:provider) {
+ provider_class.new(resource)
+ }
+
+ it 'should create instances without error' do
+ instances = provider_class.instances
+ expect(instances.class).to eq(Array)
+ expect(instances.count).to be >= 114
+ end
+
+ # if you get this error, your are missing a entry in the lsp_mapping under puppet_x/security_policy
+ # either its a type, case, or missing entry
+ it 'lsp_mapping should contain all the entries in secdata file' do
+ inffile = subject.read_policy_settings
+ missing_policies = {}
+ message = lambda {|pol| "Missing policy, check the lsp mapping for something like: #{pol}\n"}
+
+ inffile.sections.each do |section|
+ next if section == 'Unicode'
+ next if section == 'Version'
+ inffile[section].each do |name, value|
+ begin
+ SecurityPolicy.find_mapping_from_policy_name(name)
+ rescue KeyError => e
+ puts e.message
+ if value and section == 'Registry Values'
+ reg_type = value.split(',').first
+ missing_policies[name] = {:name => name, :policy_type => section, :reg_type => reg_type}
+ else
+ missing_policies[name] = {:name => name, :policy_type => section}
+ end
+ end
+ end
+ end
+ ap missing_policies
+ expect(missing_policies.count).to eq(0), "Missing policy, check the lsp mapping"
+ end
+
+ xit 'ensure instances works' do
+ instances = Puppet::Type.type(:local_security_policy).instances
+ expect(instances.count).to be > 1
+ end
+
+ describe 'write output' do
+ let(:resource) {
+ Puppet::Type.type(:local_security_policy).new(
+ :name => 'Recovery console: Allow automatic adminstrative logon',
+ :ensure => 'present',
+ :policy_setting => 'MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Setup\RecoveryConsole\SecurityLevel',
+ :policy_type => 'Registry Values',
+ :policy_value => '0')
+ }
+ xit 'should write out the file correctly' do
+
+ provider.create
+
+ end
+ end
+
+
+ describe 'resource is removed' do
+ let(:resource) {
+ Puppet::Type.type(:local_security_policy).new(
+ :name => 'Network access: Let Everyone permissions apply to anonymous users',
+ :ensure => 'absent',
+ :policy_setting => 'MACHINE\System\CurrentControlSet\Control\Lsa\EveryoneIncludesAnonymous',
+ :policy_type => 'Registry Values',
+ :policy_value => '0')
+ }
+ it 'exists? should be true' do
+ expect(provider.exists?).to eq(false)
+ # until we can implement the destroy functionality this test is useless
+ #expect(provider).to receive(:destroy).exactly(1).times
+ end
+ end
+
+ describe 'resource is present' do
+ let(:secdata) do
+ File.join(fixtures_path, 'unit', 'short_secedit.inf')
+ end
+ let(:resource) {
+ Puppet::Type.type(:local_security_policy).new(
+ :name => 'Recovery console: Allow automatic adminstrative logon',
+ :ensure => 'present',
+ :policy_setting => 'MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Setup\RecoveryConsole\SecurityLevel',
+ :policy_type => 'Registry Values',
+ :policy_value => '0')
+ }
+ it 'exists? should be true' do
+ expect(provider).to receive(:create).exactly(0).times
+ end
+ end
+
+ describe 'resource is absent' do
+ let(:resource) {
+ Puppet::Type.type(:local_security_policy).new(
+ :name => 'Recovery console: Allow automatic adminstrative logon',
+ :ensure => 'present',
+ :policy_setting => '1MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Setup\RecoveryConsole\SecurityLevel',
+ :policy_type => 'Registry Values',
+ :policy_value => '76')
+ }
+ it 'exists? should be false' do
+ expect(provider.exists?).to eq(false)
+ allow(provider).to receive(:create).exactly(1).times
+
+ end
+ end
+
+ it "should be an instance of Puppet::Type::Local_security_policy::ProviderPolicy" do
+ expect(provider).to be_an_instance_of Puppet::Type::Local_security_policy::ProviderPolicy
+ end
+end
diff --git a/modules/utilities/windows/system/local_security_policy/spec/unit/puppet/provider/local_security_policy/security_policy_spec.rb b/modules/utilities/windows/system/local_security_policy/spec/unit/puppet/provider/local_security_policy/security_policy_spec.rb
new file mode 100644
index 000000000..808f148ac
--- /dev/null
+++ b/modules/utilities/windows/system/local_security_policy/spec/unit/puppet/provider/local_security_policy/security_policy_spec.rb
@@ -0,0 +1,151 @@
+require 'spec_helper'
+require "puppet_x/lsp/security_policy"
+
+describe 'SecurityPolicy' do
+ subject { SecurityPolicy }
+ before :all do
+ Puppet::Util.stubs(:which).with("wmic").returns("c:\\tools\\wmic")
+ Puppet::Util.stubs(:which).with("secedit").returns("c:\\tools\\secedit")
+ end
+ before :each do
+ infout = StringIO.new
+ sdbout = StringIO.new
+ allow(Tempfile).to receive(:new).with('infimport').and_return(infout)
+ allow(Tempfile).to receive(:new).with('sdbimport').and_return(sdbout)
+ allow(File).to receive(:file?).with(secdata).and_return(true)
+ # the below mock seems to be required or rspec complains
+ allow(File).to receive(:file?).with(/facter/).and_return(true)
+ allow(subject).to receive(:temp_file).and_return(secdata)
+ security_policy.stubs(:wmic).with([ "useraccount", "get", "name,sid", "/format:csv"]).returns(File.read(userdata))
+ security_policy.stubs(:wmic).with([ "group", "get", "name,sid", "/format:csv"]).returns(File.read(groupdata))
+ allow_any_instance_of(SecurityPolicy).to receive(:wmic).with([ "useraccount", "get", "name,sid", "/format:csv"]).and_return(userdata)
+ allow_any_instance_of(SecurityPolicy).to receive(:wmic).with([ "group", "get", "name,sid", "/format:csv"]).and_return(groupdata)
+
+ subject.stubs(:secedit).with(['/configure', '/db', 'sdbout', '/cfg', 'infout', '/quiet'])
+ subject.stubs(:secedit).with(['/export', '/cfg', secdata, '/quiet'])
+ end
+
+ let(:secdata) do
+ File.join(fixtures_path, 'unit', 'secedit.inf')
+ end
+
+ let(:groupdata) do
+ File.join(fixtures_path, 'unit', 'group.txt')
+ end
+
+ let(:userdata) do
+ File.join(fixtures_path, 'unit', 'useraccount.txt')
+ end
+
+ let(:security_policy){
+ SecurityPolicy.new
+ }
+
+ it 'should return builtin accounts' do
+ # we just want to check that this is an array within an array that has 3 elements in each element
+ expect(security_policy.builtin_accounts.count).to be > 50
+ expect(security_policy.builtin_accounts.first.count).to eq(3)
+ end
+
+ it 'user_sid_array should return array' do
+ a = security_policy.user_sid_array
+ # we just want to check that this is an array within an array that has 3 elements in each element
+ expect(a.count).to be > 50
+ expect(a.first.count).to eq(3)
+ end
+
+ it 'local accounts should return array' do
+ a = security_policy.local_accounts
+ expect(a).to be_instance_of(Array)
+ expect(a.count).to eq(19)
+ expect(a.first.count).to eq(3)
+ end
+
+ it 'should return user' do
+ expect(security_policy.sid_to_user("S-1-5-32-556")).to eq('Network Configuration Operators')
+ expect(security_policy.sid_to_user('*S-1-5-80-0')).to eq("NT_SERVICE\\ALL_SERVICES")
+ end
+
+ it 'should return sid when user is not found' do
+ expect(security_policy.user_to_sid('*S-11-5-80-0')).to eq('*S-11-5-80-0')
+ end
+
+ it 'should return sid' do
+ expect(security_policy.user_to_sid("Network Configuration Operators")).to eq('*S-1-5-32-556')
+ expect(security_policy.user_to_sid("NT_SERVICE\\ALL_SERVICES")).to eq('*S-1-5-80-0')
+ end
+
+ it 'should return user when sid is not found' do
+ expect(security_policy.user_to_sid("N_SERVICE\\ALL_SERVICES")).to eq("N_SERVICE\\ALL_SERVICES")
+ end
+
+
+ describe 'registry value' do
+ let(:resource) {
+ Puppet::Type.type(:local_security_policy).new(
+ :name => 'Network access: Let Everyone permissions apply to anonymous users',
+ :ensure => 'present',
+ :policy_setting => 'MACHINE\System\CurrentControlSet\Control\Lsa\EveryoneIncludesAnonymous',
+ :policy_type => 'Registry Values',
+ :policy_value => '3')
+ }
+ it 'should convert a registry value' do
+ expect(subject.convert_registry_value("Network access: Let Everyone permissions apply to anonymous users",
+ 3)).to eq('4,3')
+ end
+
+ it 'should convert a policy right' do
+ defined_policy = {
+ :name => 'Network access: Let Everyone permissions apply to anonymous users',
+ :ensure => 'present',
+ :policy_setting => 'MACHINE\System\CurrentControlSet\Control\Lsa\EveryoneIncludesAnonymous',
+ :policy_type => 'Registry Values',
+ :policy_value => '3'
+ }
+ hash = security_policy.convert_policy_hash(defined_policy)
+ expect(hash[:policy_value]).to eq('4,3')
+
+ end
+ end
+ #
+ describe 'privilege right' do
+ let(:resource) {
+ Puppet::Type.type(:local_security_policy).new(
+ :name => 'Access this computer from the network',
+ :ensure => 'present',
+ :policy_setting => 'SeNetworkLogonRight',
+ :policy_type => 'Privilege Rights',
+ :policy_value => 'AUTHENTICATED_USERS,BUILTIN_ADMINISTRATORS'
+ )
+ }
+ it 'should convert a privilege right to sids' do
+ hash = security_policy.convert_policy_hash(resource)
+ expect(hash[:policy_value]).to eq('*S-1-5-11,*S-1-5-32-544')
+ end
+
+ end
+ #
+ # describe 'audit event' do
+ # let(:resource) {
+ # Puppet::Type.type(:local_security_policy).new(
+ # :name => 'Audit account logon events',
+ # :ensure => 'present',
+ # :policy_setting => "AuditAccountLogon",
+ # :policy_type => "Event Audit",
+ # :policy_value => 'Success,Failure',
+ # )
+ # }
+ # it 'should convert a audit right' do
+ # defined_policy = SecurityPolicy.find_mapping_from_policy_desc(resource[:name])
+ # defined_policy.merge!(resource.to_hash)
+ # expect(provider.convert_audit(defined_policy)).to eq(3)
+ # end
+ #
+ # it 'should convert a audit right' do
+ # defined_policy = SecurityPolicy.find_mapping_from_policy_desc(resource[:name])
+ # defined_policy.merge!(resource.to_hash)
+ # hash = provider.convert_policy_hash(defined_policy)
+ # expect(hash[:policy_value]).to eq(3)
+ # end
+ # end
+end
diff --git a/modules/utilities/windows/system/local_security_policy/spec/unit/puppet/type/local_security_policy/local_security_policy_spec.rb b/modules/utilities/windows/system/local_security_policy/spec/unit/puppet/type/local_security_policy/local_security_policy_spec.rb
new file mode 100644
index 000000000..cd5a00552
--- /dev/null
+++ b/modules/utilities/windows/system/local_security_policy/spec/unit/puppet/type/local_security_policy/local_security_policy_spec.rb
@@ -0,0 +1,50 @@
+require 'spec_helper'
+
+describe Puppet::Type.type(:local_security_policy) do
+ [:name].each do |param|
+ it "should have a #{param} parameter" do
+ expect(Puppet::Type.type(:local_security_policy).attrtype(param)).to eq(:param)
+ end
+ end
+
+ [:policy_type,:ensure, :ensure, :policy_setting, :policy_value].each do |param|
+ it "should have an #{param} property" do
+ expect(Puppet::Type.type(:local_security_policy).attrtype(param)).to eq(:property)
+ end
+ end
+
+ describe 'test validation' do
+ it 'can create a registry value' do
+ resource = Puppet::Type.type(:local_security_policy).new(
+ :name => 'Network access: Let Everyone permissions apply to anonymous users',
+ :ensure => 'present',
+ :policy_setting => 'MACHINE\System\CurrentControlSet\Control\Lsa\EveryoneIncludesAnonymous',
+ :policy_type => 'Registry Values',
+ :policy_value => '0')
+ expect(resource[:policy_value]).to eq('4,0')
+ end
+
+ it 'raises an error with bad policy event audit value' do
+ expect{
+ Puppet::Type.type(:local_security_policy).new(
+ :name => 'Audit account logon events',
+ :ensure => 'present',
+ :policy_setting => "AuditAccountLogon",
+ :policy_type => "Event Audit",
+ :policy_value => 'Success,Failure',
+ )}.to raise_error(Puppet::ResourceError)
+ end
+
+ it 'raises an error with with bad value for Administrator account status' do
+ expect {
+ Puppet::Type.type(:local_security_policy).new(
+ :name => 'Administrator account status',
+ :ensure => present,
+ :policy_setting => 'EnableAdminAccount',
+ :policy_type => 'System Access',
+ :policy_value => '0'
+ )
+ }.to raise_error(Puppet::ResourceError)
+ end
+ end
+end
diff --git a/modules/utilities/windows/system/local_security_policy/tests/init.pp b/modules/utilities/windows/system/local_security_policy/tests/init.pp
new file mode 100644
index 000000000..c3101a6f7
--- /dev/null
+++ b/modules/utilities/windows/system/local_security_policy/tests/init.pp
@@ -0,0 +1,10 @@
+
+local_security_policy { 'Allow log on locally':
+ ensure => 'present',
+ policy_value => 'Administrators, Users',
+}
+
+local_security_policy { 'Maximum password age':
+ ensure => 'present',
+ policy_value => '90',
+}
diff --git a/modules/utilities/windows/system/parameterised_accounts/manifests/account.pp b/modules/utilities/windows/system/parameterised_accounts/manifests/account.pp
new file mode 100644
index 000000000..fface523b
--- /dev/null
+++ b/modules/utilities/windows/system/parameterised_accounts/manifests/account.pp
@@ -0,0 +1,46 @@
+define parameterised_accounts::account (
+ $username,
+ $password,
+ $super_user,
+ $strings_to_leak,
+ $leaked_filenames,
+ $data_to_leak = []
+) {
+ # Add user account
+
+ if $super_user {
+ $groups = ['Users','Administrators']
+ } else {
+ $groups = ['Users']
+ }
+
+ user { $username:
+ name => $username,
+ ensure => present,
+ groups => $groups,
+ password => $password,
+ managehome => true,
+ } ->
+
+ # Leak strings in a text file in the users home directory
+ ::secgen_functions::leak_files { "$username-file-leak":
+ storage_directory => "C:/Users/$username/Desktop",
+ strings_to_leak => $strings_to_leak,
+ leaked_filenames => $leaked_filenames,
+ owner => $username,
+ group => $username,
+ mode => '0444',
+ leaked_from => "accounts_$username",
+ }
+
+ unless $data_to_leak == undef or $data_to_leak == [] or $data_to_leak == [''] {
+ ::secgen_functions::leak_data { "$username-data-leak":
+ storage_directory => "C:/Users/$username/Desktop",
+ data_to_leak => $data_to_leak,
+ owner => $username,
+ group => $username,
+ mode => '0444',
+ leaked_from => "accounts_$username",
+ }
+ }
+}
diff --git a/modules/utilities/windows/system/parameterised_accounts/manifests/init.pp b/modules/utilities/windows/system/parameterised_accounts/manifests/init.pp
new file mode 100644
index 000000000..e896aeb48
--- /dev/null
+++ b/modules/utilities/windows/system/parameterised_accounts/manifests/init.pp
@@ -0,0 +1,27 @@
+class parameterised_accounts::init {
+ $secgen_parameters = secgen_functions::get_parameters($::base64_inputs_file)
+ $accounts = $secgen_parameters['accounts']
+
+ local_security_policy { 'Password must meet complexity requirements':
+ ensure => present,
+ policy_value => 'Disabled',
+ }
+
+ if $accounts {
+ notice($accounts)
+ $accounts.each |$raw_account| {
+ $account = parsejson($raw_account)
+ $username = $account['username']
+ parameterised_accounts::account { "parameterised_$username":
+ username => $username,
+ password => $account['password'],
+ super_user => str2bool($account['super_user']),
+ strings_to_leak => $account['strings_to_leak'],
+ leaked_filenames => $account['leaked_filenames'],
+ data_to_leak => $account['data_to_leak'],
+ }
+ }
+ }
+
+
+}
diff --git a/modules/utilities/windows/system/parameterised_accounts/parameterised_accounts.pp b/modules/utilities/windows/system/parameterised_accounts/parameterised_accounts.pp
new file mode 100644
index 000000000..05a95e975
--- /dev/null
+++ b/modules/utilities/windows/system/parameterised_accounts/parameterised_accounts.pp
@@ -0,0 +1 @@
+require parameterised_accounts::init
diff --git a/modules/utilities/windows/system/parameterised_accounts/secgen_metadata.xml b/modules/utilities/windows/system/parameterised_accounts/secgen_metadata.xml
new file mode 100644
index 000000000..0743c1223
--- /dev/null
+++ b/modules/utilities/windows/system/parameterised_accounts/secgen_metadata.xml
@@ -0,0 +1,32 @@
+
+
+
+ Parameterised Accounts Module
+ Thomas Shaw
+ Puppet Labs
+ Apache v2
+ Parameterised user account creation and modification. Adds an account with a strong randomly generated password
+ by default.
+
+
+ system
+ windows
+
+
+ https://forge.puppet.com/puppetlabs/accounts
+
+ accounts
+
+
+ bases/(debian|kali|ubuntu).*
+
+
+
+ .*local_security_policy
+
+
+
+
+
diff --git a/scenarios/default_scenario.xml b/scenarios/default_scenario.xml
index e6678aab4..272ad23cb 100644
--- a/scenarios/default_scenario.xml
+++ b/scenarios/default_scenario.xml
@@ -7,7 +7,7 @@
escalation
-
+
diff --git a/scenarios/examples/windows_scenario_accounts.xml b/scenarios/examples/windows_scenario_accounts.xml
new file mode 100644
index 000000000..2ad408ef5
--- /dev/null
+++ b/scenarios/examples/windows_scenario_accounts.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+ storage_server
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ challenger
+
+
+ hello
+
+
+ false
+
+
+ This is a test
+
+
+ test.txt
+
+
+
+
+ challenger2
+
+
+ tiaspbiqe2r
+
+
+ false
+
+
+ <emails
+
+
+ test.txt
+
+
+
+
+
+
+
+
+
+