summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Smith <tsmith@chef.io>2018-02-26 12:10:51 -0800
committerGitHub <noreply@github.com>2018-02-26 12:10:51 -0800
commitdef7b6fbf871de2a4d24f8860e59d3487f69eb18 (patch)
tree76f830aefe4841bf9710213336099f8d432ad350
parent67c23662d211e6d95ccb6cd81890352a13b7228a (diff)
parent7fa099aeedf0dbb05df6dedf1a1d711c36bd0b83 (diff)
downloadchef-def7b6fbf871de2a4d24f8860e59d3487f69eb18.tar.gz
Merge pull request #6767 from chef/windows_resources
Add windows auto_run, font, pagefile, printer, printer_port, and shortcut resources
-rw-r--r--lib/chef/resource/windows_auto_run.rb87
-rw-r--r--lib/chef/resource/windows_font.rb127
-rw-r--r--lib/chef/resource/windows_pagefile.rb223
-rw-r--r--lib/chef/resource/windows_printer.rb147
-rw-r--r--lib/chef/resource/windows_printer_port.rb132
-rw-r--r--lib/chef/resource/windows_shortcut.rb79
-rw-r--r--lib/chef/resources.rb8
-rw-r--r--spec/unit/resource/windows_auto_run_spec.rb50
-rw-r--r--spec/unit/resource/windows_font_spec.rb44
-rw-r--r--spec/unit/resource/windows_pagefile_spec.rb45
-rw-r--r--spec/unit/resource/windows_printer_port_spec.rb45
-rw-r--r--spec/unit/resource/windows_printer_spec.rb45
-rw-r--r--spec/unit/resource/windows_shortcut_spec.rb39
13 files changed, 1070 insertions, 1 deletions
diff --git a/lib/chef/resource/windows_auto_run.rb b/lib/chef/resource/windows_auto_run.rb
new file mode 100644
index 0000000000..9e5414f26e
--- /dev/null
+++ b/lib/chef/resource/windows_auto_run.rb
@@ -0,0 +1,87 @@
+#
+# Author:: Paul Morton (<pmorton@biaprotect.com>)
+# Copyright:: 2011-2018, Business Intelligence Associates, Inc.
+# Copyright:: 2017-2018, Chef Software, Inc.
+#
+# 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.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class WindowsAutorun < Chef::Resource
+ resource_name :windows_auto_run
+ provides :windows_auto_run
+
+ description "Use the windows_auto_run resource to set applications to run at logon."
+ introduced "14.0"
+
+ property :program_name, String,
+ description: "The name of the program to run at login if different from the resource name.",
+ name_property: true
+
+ property :path, String,
+ coerce: proc { |x| x.tr("/", "\\") }, # make sure we have windows paths for the registry
+ description: "The path to the program to be run at login."
+
+ property :args, String,
+ description: "Any arguments for the program."
+
+ property :root, Symbol,
+ description: "The registry root key to put the entry under.",
+ equal_to: %i{machine user},
+ default: :machine
+
+ alias_method :program, :path
+
+ action :create do
+ description "Create an item to be run at login."
+
+ data = "\"#{new_resource.path}\""
+ data << " #{new_resource.args}" if new_resource.args
+
+ declare_resource(:registry_key, registry_path) do
+ values [{
+ name: new_resource.program_name,
+ type: :string,
+ data: data,
+ }]
+ action :create
+ end
+ end
+
+ action :remove do
+ description "Remove an item that was previously setup to run at login"
+
+ declare_resource(:registry_key, registry_path) do
+ values [{
+ name: new_resource.program_name,
+ type: :string,
+ data: "",
+ }]
+ action :delete
+ end
+ end
+
+ action_class do
+ # determine the full registry path based on the root property
+ # @return [String]
+ def registry_path
+ { machine: "HKLM", user: "HKCU" }[new_resource.root] + \
+ '\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run'
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_font.rb b/lib/chef/resource/windows_font.rb
new file mode 100644
index 0000000000..a1f7fa153b
--- /dev/null
+++ b/lib/chef/resource/windows_font.rb
@@ -0,0 +1,127 @@
+#
+# Copyright:: 2014-2018, Schuberg Philis BV.
+# Copyright:: 2017-2018, Chef Software, Inc.
+#
+# 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.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class WindowsFont < Chef::Resource
+ require "chef/util/path_helper"
+
+ resource_name :windows_font
+ provides :windows_font
+
+ description "Use the windows_font resource to install or remove font files on Windows."\
+ " Sources the font by default from the cookbook using the resource, but a URI"\
+ " source can be specified as well."
+ introduced "14.0"
+
+ property :font_name, String,
+ description: "The file name of the font file name to install if different than the resource name.",
+ name_property: true
+
+ property :source, String,
+ description: "A local filesystem path or URI to source the font file from.",
+ coerce: proc { |x| x =~ /^.:.*/ ? x.tr('\\', "/").gsub("//", "/") : x }
+
+ action :install do
+ description "Install a font to the system fonts directory."
+
+ if font_exists?
+ Chef::Log.debug("Not installing font: #{new_resource.font_name} as font already installed.")
+ else
+ retrieve_cookbook_font
+ install_font
+ del_cookbook_font
+ end
+ end
+
+ action_class do
+ # if a source is specified fetch using remote_file. If not use cookbook_file
+ def retrieve_cookbook_font
+ font_file = new_resource.font_name
+ if new_resource.source
+ declare_resource(:remote_file, font_file) do
+ action :nothing
+ source source_uri
+ path Chef::Util::PathHelper.join(ENV["TEMP"], font_file)
+ end.run_action(:create)
+ else
+ declare_resource(:cookbook_file, font_file) do
+ action :nothing
+ cookbook cookbook_name.to_s unless cookbook_name.nil?
+ path Chef::Util::PathHelper.join(ENV["TEMP"], font_file)
+ end.run_action(:create)
+ end
+ end
+
+ # delete the temp cookbook file
+ def del_cookbook_font
+ file Chef::Util::PathHelper.join(ENV["TEMP"], new_resource.font_name) do
+ action :delete
+ end
+ end
+
+ # install the font into the appropriate fonts directory
+ def install_font
+ require "win32ole" if RUBY_PLATFORM =~ /mswin|mingw32|windows/
+ fonts_dir = Chef::Util::PathHelper.join(ENV["windir"], "fonts")
+ folder = WIN32OLE.new("Shell.Application").Namespace(fonts_dir)
+ converge_by("install font #{new_resource.font_name} to #{fonts_dir}") do
+ folder.CopyHere(Chef::Util::PathHelper.join(ENV["TEMP"], new_resource.font_name))
+ end
+ end
+
+ # Check to see if the font is installed in the fonts dir
+ #
+ # @return [Boolean] Is the font is installed?
+ def font_exists?
+ require "win32ole" if RUBY_PLATFORM =~ /mswin|mingw32|windows/
+ fonts_dir = WIN32OLE.new("WScript.Shell").SpecialFolders("Fonts")
+ Chef::Log.debug("Seeing if the font at #{Chef::Util::PathHelper.join(fonts_dir, new_resource.font_name)} exists")
+ ::File.exist?(Chef::Util::PathHelper.join(fonts_dir, new_resource.font_name))
+ end
+
+ # Parse out the schema provided to us to see if it's one we support via remote_file.
+ # We do this because URI will parse C:/foo as schema 'c', which won't work with remote_file
+ #
+ # @return [Boolean]
+ def remote_file_schema?(schema)
+ return true if %w{http https ftp}.include?(schema)
+ end
+
+ # return new_resource.source if we have a proper URI specified
+ # if it's a local file listed as a source return it in file:// format
+ #
+ # @return [String] path to the font
+ def source_uri
+ begin
+ require "uri"
+ if remote_file_schema?(URI.parse(new_resource.source).scheme)
+ Chef::Log.debug("source property starts with ftp/http. Using source property unmodified")
+ return new_resource.source
+ end
+ rescue URI::InvalidURIError
+ Chef::Log.warn("source property of #{new_resource.source} could not be processed as a URI. Check the format you provided.")
+ end
+ Chef::Log.debug("source property does not start with ftp/http. Prepending with file:// as it appears to be a local file.")
+ "file://#{new_resource.source}"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_pagefile.rb b/lib/chef/resource/windows_pagefile.rb
new file mode 100644
index 0000000000..069e76107c
--- /dev/null
+++ b/lib/chef/resource/windows_pagefile.rb
@@ -0,0 +1,223 @@
+#
+# Copyright:: 2012-2018, Nordstrom, Inc.
+# Copyright:: 2017-2018, Chef Software, Inc.
+#
+# 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.
+#
+
+class Chef
+ class Resource
+ class WindowsPagefile < Chef::Resource
+ resource_name :windows_pagefile
+ provides :windows_pagefile
+
+ description "Use the windows_pagefile resource to configure pagefile settings on Windows."
+ introduced "14.0"
+
+ property :path, String,
+ coerce: proc { |x| x.tr('\\', "/") },
+ description: "The path to the pagefile if different from the resource name.",
+ name_property: true
+
+ property :system_managed, [true, false],
+ description: "Configures whether the system manages the pagefile size."
+
+ property :automatic_managed, [true, false],
+ description: "Enable automatic management of pagefile initial and maximum size. Setting this to true ignores 'initial_size' and 'maximum_size' properties.",
+ default: false
+
+ property :initial_size, Integer,
+ description: "Initial size of the pagefile in bytes."
+
+ property :maximum_size, Integer,
+ description: "Maximum size of the pagefile in bytes."
+
+ action :set do
+ description "Configures the default pagefile, creating if it doesn't exist."
+
+ pagefile = new_resource.path
+ initial_size = new_resource.initial_size
+ maximum_size = new_resource.maximum_size
+ system_managed = new_resource.system_managed
+ automatic_managed = new_resource.automatic_managed
+
+ if automatic_managed
+ set_automatic_managed unless automatic_managed?
+ else
+ unset_automatic_managed if automatic_managed?
+
+ # Check that the resource is not just trying to unset automatic managed, if it is do nothing more
+ if (initial_size && maximum_size) || system_managed
+ validate_name
+ create(pagefile) unless exists?(pagefile)
+
+ if system_managed
+ set_system_managed(pagefile) unless max_and_min_set?(pagefile, 0, 0)
+ else
+ unless max_and_min_set?(pagefile, initial_size, maximum_size)
+ set_custom_size(pagefile, initial_size, maximum_size)
+ end
+ end
+ end
+ end
+ end
+
+ action :delete do
+ description "Deletes the specified pagefile."
+
+ validate_name
+ delete(new_resource.path) if exists?(new_resource.path)
+ end
+
+ action_class do
+ # account for Window's wacky File System Redirector
+ # http://msdn.microsoft.com/en-us/library/aa384187(v=vs.85).aspx
+ # especially important for 32-bit processes (like Ruby) on a
+ # 64-bit instance of Windows.
+ def locate_sysnative_cmd(cmd)
+ if ::File.exist?("#{ENV['WINDIR']}\\sysnative\\#{cmd}")
+ "#{ENV['WINDIR']}\\sysnative\\#{cmd}"
+ elsif ::File.exist?("#{ENV['WINDIR']}\\system32\\#{cmd}")
+ "#{ENV['WINDIR']}\\system32\\#{cmd}"
+ else
+ cmd
+ end
+ end
+
+ # make sure the provided name property matches the appropriate format
+ # we do this here and not in the property itself because if automatic_managed
+ # is set then this validation is not necessary / doesn't make sense at all
+ def validate_name
+ return if /^.:.*.sys/ =~ new_resource.path
+ raise "#{new_resource.path} does not match the format DRIVE:\\path\\file.sys for pagefiles. Example: C:\\pagefile.sys"
+ end
+
+ # See if the pagefile exists
+ #
+ # @param [String] pagefile path to the pagefile
+ # @return [Boolean]
+ def exists?(pagefile)
+ @exists ||= begin
+ Chef::Log.debug("Checking if #{pagefile} exists by runing: #{wmic} pagefileset where SettingID=\"#{get_setting_id(pagefile)}\" list /format:list")
+ cmd = shell_out("#{wmic} pagefileset where SettingID=\"#{get_setting_id(pagefile)}\" list /format:list", returns: [0])
+ cmd.stderr.empty? && (cmd.stdout =~ /SettingID=#{get_setting_id(pagefile)}/i)
+ end
+ end
+
+ # is the max/min pagefile size set?
+ #
+ # @param [String] pagefile path to the pagefile
+ # @param [String] min the minimum size of the pagefile
+ # @param [String] max the minimum size of the pagefile
+ # @return [Boolean]
+ def max_and_min_set?(pagefile, min, max)
+ @max_and_min_set ||= begin
+ Chef::Log.debug("Checking if #{pagefile} min: #{min} and max #{max} are set")
+ cmd = shell_out("#{wmic} pagefileset where SettingID=\"#{get_setting_id(pagefile)}\" list /format:list", returns: [0])
+ cmd.stderr.empty? && (cmd.stdout =~ /InitialSize=#{min}/i) && (cmd.stdout =~ /MaximumSize=#{max}/i)
+ end
+ end
+
+ # create a pagefile
+ #
+ # @param [String] pagefile path to the pagefile
+ def create(pagefile)
+ converge_by("create pagefile #{pagefile}") do
+ Chef::Log.debug("Running #{wmic} pagefileset create name=\"#{win_friendly_path(pagefile)}\"")
+ cmd = shell_out("#{wmic} pagefileset create name=\"#{win_friendly_path(pagefile)}\"")
+ check_for_errors(cmd.stderr)
+ end
+ end
+
+ # delete a pagefile
+ #
+ # @param [String] pagefile path to the pagefile
+ def delete(pagefile)
+ converge_by("remove pagefile #{pagefile}") do
+ Chef::Log.debug("Running #{wmic} pagefileset where SettingID=\"#{get_setting_id(pagefile)}\" delete")
+ cmd = shell_out("#{wmic} pagefileset where SettingID=\"#{get_setting_id(pagefile)}\" delete")
+ check_for_errors(cmd.stderr)
+ end
+ end
+
+ # see if the pagefile is automatically managed by Windows
+ #
+ # @return [Boolean]
+ def automatic_managed?
+ @automatic_managed ||= begin
+ Chef::Log.debug("Checking if pagefiles are automatically managed")
+ cmd = shell_out("#{wmic} computersystem where name=\"%computername%\" get AutomaticManagedPagefile /format:list")
+ cmd.stderr.empty? && (cmd.stdout =~ /AutomaticManagedPagefile=TRUE/i)
+ end
+ end
+
+ # turn on automatic management of all pagefiles by Windows
+ def set_automatic_managed
+ converge_by("set pagefile to Automatic Managed") do
+ Chef::Log.debug("Running #{wmic} computersystem where name=\"%computername%\" set AutomaticManagedPagefile=True")
+ cmd = shell_out("#{wmic} computersystem where name=\"%computername%\" set AutomaticManagedPagefile=True")
+ check_for_errors(cmd.stderr)
+ end
+ end
+
+ # turn off automatic management of all pagefiles by Windows
+ def unset_automatic_managed
+ converge_by("set pagefile to User Managed") do
+ Chef::Log.debug("Running #{wmic} computersystem where name=\"%computername%\" set AutomaticManagedPagefile=False")
+ cmd = shell_out("#{wmic} computersystem where name=\"%computername%\" set AutomaticManagedPagefile=False")
+ check_for_errors(cmd.stderr)
+ end
+ end
+
+ # set a custom size for the pagefile (vs the defaults)
+ #
+ # @param [String] pagefile path to the pagefile
+ # @param [String] min the minimum size of the pagefile
+ # @param [String] max the minimum size of the pagefile
+ def set_custom_size(pagefile, min, max)
+ converge_by("set #{pagefile} to InitialSize=#{min} & MaximumSize=#{max}") do
+ Chef::Log.debug("Running #{wmic} pagefileset where SettingID=\"#{get_setting_id(pagefile)}\" set InitialSize=#{min},MaximumSize=#{max}")
+ cmd = shell_out("#{wmic} pagefileset where SettingID=\"#{get_setting_id(pagefile)}\" set InitialSize=#{min},MaximumSize=#{max}", returns: [0])
+ check_for_errors(cmd.stderr)
+ end
+ end
+
+ # set a pagefile size to be system managed
+ #
+ # @param [String] pagefile path to the pagefile
+ def set_system_managed(pagefile)
+ converge_by("set #{pagefile} to System Managed") do
+ Chef::Log.debug("Running #{wmic} pagefileset where SettingID=\"#{get_setting_id(pagefile)}\" set InitialSize=0,MaximumSize=0")
+ cmd = shell_out("#{wmic} pagefileset where SettingID=\"#{get_setting_id(pagefile)}\" set InitialSize=0,MaximumSize=0", returns: [0])
+ check_for_errors(cmd.stderr)
+ end
+ end
+
+ def get_setting_id(pagefile)
+ pagefile = win_friendly_path(pagefile)
+ pagefile = pagefile.split('\\')
+ "#{pagefile[1]} @ #{pagefile[0]}"
+ end
+
+ # raise if there's an error on stderr on a shellout
+ def check_for_errors(stderr)
+ raise stderr.chomp unless stderr.empty?
+ end
+
+ def wmic
+ @wmic ||= locate_sysnative_cmd("wmic.exe")
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_printer.rb b/lib/chef/resource/windows_printer.rb
new file mode 100644
index 0000000000..c134e27bf7
--- /dev/null
+++ b/lib/chef/resource/windows_printer.rb
@@ -0,0 +1,147 @@
+#
+# Author:: Doug Ireton (<doug@1strategy.com>)
+# Copyright:: 2012-2018, Nordstrom, Inc.
+#
+# 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.
+#
+# See here for more info:
+# http://msdn.microsoft.com/en-us/library/windows/desktop/aa394492(v=vs.85).aspx
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class WindowsPrinter < Chef::Resource
+ require "resolv"
+
+ resource_name :windows_printer
+ provides :windows_printer
+
+ description "Use the windows_printer resource to setup Windows printers. Note"\
+ " that this doesn't currently install a printer driver. You must"\
+ " already have the driver installed on the system."
+ introduced "14.0"
+
+ property :device_id, String,
+ description: "Printer queue name, e.g. 'HP LJ 5200 in fifth floor copy room'.",
+ name_property: true
+
+ property :comment, String,
+ description: "Optional descriptor for the printer queue."
+
+ property :default, [true, false],
+ description: "Should this be the system's default printer.",
+ default: false
+
+ property :driver_name, String,
+ description: "Exact name of printer driver. Note that the printer driver must already be installed on the node.",
+ required: true
+
+ property :location, String,
+ description: "Printer location, e.g. 'Fifth floor copy room'."
+
+ property :shared, [true, false],
+ description: "Should the printer be shared.",
+ default: false
+
+ property :share_name, String,
+ description: "The name to share the printer as."
+
+ property :ipv4_address, String,
+ description: "Printer IPv4 address, e.g. '10.4.64.23'.",
+ validation_message: "The ipv4_address property must be in the IPv4 format of WWW.XXX.YYY.ZZZ",
+ regex: Resolv::IPv4::Regex
+
+ property :exists, [true, false], desired_state: true
+
+ PRINTERS_REG_KEY = 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\\'.freeze unless defined?(PRINTERS_REG_KEY)
+
+ # does the printer exist
+ #
+ # @param [String] name the name of the printer
+ # @return [Boolean]
+ def printer_exists?(name)
+ printer_reg_key = PRINTERS_REG_KEY + name
+ Chef::Log.debug "Checking to see if this reg key exists: '#{printer_reg_key}'"
+ registry_key_exists?(printer_reg_key)
+ end
+
+ # @todo Set @current_resource printer properties from registry
+ load_current_value do |desired|
+ name desired.name
+ exists printer_exists?(desired.name)
+ end
+
+ action :create do
+ description "Create a new printer and a printer port if one doesn't already exist."
+
+ if @current_resource.exists
+ Chef::Log.info "#{@new_resource} already exists - nothing to do."
+ else
+ converge_by("Create #{@new_resource}") do
+ create_printer
+ end
+ end
+ end
+
+ action :delete do
+ description "Delete an existing printer. Note this does not delete the associated printer port."
+
+ if @current_resource.exists
+ converge_by("Delete #{@new_resource}") do
+ delete_printer
+ end
+ else
+ Chef::Log.info "#{@current_resource} doesn't exist - can't delete."
+ end
+ end
+
+ action_class do
+ # creates the printer port and then the printer
+ def create_printer
+ # Create the printer port first
+ windows_printer_port new_resource.ipv4_address do
+ end
+
+ port_name = "IP_#{new_resource.ipv4_address}"
+
+ declare_resource(:powershell_script, "Creating printer: #{new_resource.name}") do
+ code <<-EOH
+
+ Set-WmiInstance -class Win32_Printer `
+ -EnableAllPrivileges `
+ -Argument @{ DeviceID = "#{new_resource.device_id}";
+ Comment = "#{new_resource.comment}";
+ Default = "$#{new_resource.default}";
+ DriverName = "#{new_resource.driver_name}";
+ Location = "#{new_resource.location}";
+ PortName = "#{port_name}";
+ Shared = "$#{new_resource.shared}";
+ ShareName = "#{new_resource.share_name}";
+ }
+ EOH
+ end
+ end
+
+ def delete_printer
+ declare_resource(:powershell_script, "Deleting printer: #{new_resource.name}") do
+ code <<-EOH
+ $printer = Get-WMIObject -class Win32_Printer -EnableAllPrivileges -Filter "name = '#{new_resource.name}'"
+ $printer.Delete()
+ EOH
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_printer_port.rb b/lib/chef/resource/windows_printer_port.rb
new file mode 100644
index 0000000000..9da43948f6
--- /dev/null
+++ b/lib/chef/resource/windows_printer_port.rb
@@ -0,0 +1,132 @@
+#
+# Author:: Doug Ireton <doug@1strategy.com>
+# Copyright:: 2012-2018, Nordstrom, Inc.
+#
+# 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.
+#
+# See here for more info:
+# http://msdn.microsoft.com/en-us/library/windows/desktop/aa394492(v=vs.85).aspx
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class WindowsPrinterPort < Chef::Resource
+ require "resolv"
+
+ resource_name :windows_printer_port
+ provides :windows_printer_port
+
+ description "Use the windows_printer_port resoruce to create and delete TCP/IPv4 printer ports on Windows"
+ introduced "14.0"
+
+ property :ipv4_address, String,
+ name_property: true,
+ regex: Resolv::IPv4::Regex,
+ validation_message: "The ipv4_address property must be in the format of WWW.XXX.YYY.ZZZ!",
+ description: "The IPv4 address of the printer port."
+
+ property :port_name, String,
+ description: "The port name."
+
+ property :port_number, Integer,
+ description: "The port number.",
+ default: 9100
+
+ property :port_description, String,
+ description: "The description of the port."
+
+ property :snmp_enabled, [true, false],
+ description: "Should SNMP be enabled on the port.",
+ default: false
+
+ property :port_protocol, Integer,
+ description: "The printer port protocol, 1 (RAW), or 2 (LPR).",
+ validation_message: "port_protocol must be either 1 for RAW or 2 for LPR!",
+ default: 1, equal_to: [1, 2]
+
+ property :exists, [true, false],
+ desired_state: true
+
+ PORTS_REG_KEY = 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Print\Monitors\Standard TCP/IP Port\Ports\\'.freeze unless defined?(PORTS_REG_KEY)
+
+ def port_exists?(name)
+ port_reg_key = PORTS_REG_KEY + name
+
+ Chef::Log.debug "Checking to see if this reg key exists: '#{port_reg_key}'"
+ registry_key_exists?(port_reg_key)
+ end
+
+ # @todo Set @current_resource port properties from registry
+ load_current_value do |desired|
+ name desired.name
+ ipv4_address desired.ipv4_address
+ port_name desired.port_name || "IP_#{desired.ipv4_address}"
+ exists port_exists?(desired.port_name || "IP_#{desired.ipv4_address}")
+ end
+
+ action :create do
+ if current_resource.exists
+ Chef::Log.info "#{@new_resource} already exists - nothing to do."
+ else
+ converge_by("Create #{@new_resource}") do
+ create_printer_port
+ end
+ end
+ end
+
+ action :delete do
+ if current_resource.exists
+ converge_by("Delete #{@new_resource}") do
+ delete_printer_port
+ end
+ else
+ Chef::Log.info "#{@current_resource} doesn't exist - can't delete."
+ end
+ end
+
+ action_class do
+ def create_printer_port
+ port_name = new_resource.port_name || "IP_#{new_resource.ipv4_address}"
+
+ # create the printer port using PowerShell
+ declare_resource(:powershell_script, "Creating printer port #{new_resource.port_name}") do
+ code <<-EOH
+
+ Set-WmiInstance -class Win32_TCPIPPrinterPort `
+ -EnableAllPrivileges `
+ -Argument @{ HostAddress = "#{new_resource.ipv4_address}";
+ Name = "#{port_name}";
+ Description = "#{new_resource.port_description}";
+ PortNumber = "#{new_resource.port_number}";
+ Protocol = "#{new_resource.port_protocol}";
+ SNMPEnabled = "$#{new_resource.snmp_enabled}";
+ }
+ EOH
+ end
+ end
+
+ def delete_printer_port
+ port_name = new_resource.port_name || "IP_#{new_resource.ipv4_address}"
+
+ declare_resource(:powershell_script, "Deleting printer port: #{new_resource.port_name}") do
+ code <<-EOH
+ $port = Get-WMIObject -class Win32_TCPIPPrinterPort -EnableAllPrivileges -Filter "name = '#{port_name}'"
+ $port.Delete()
+ EOH
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_shortcut.rb b/lib/chef/resource/windows_shortcut.rb
new file mode 100644
index 0000000000..7e88a8c460
--- /dev/null
+++ b/lib/chef/resource/windows_shortcut.rb
@@ -0,0 +1,79 @@
+#
+# Author:: Doug MacEachern <dougm@vmware.com>
+# Copyright:: 2010-2018, VMware, Inc.
+# Copyright:: 2017-2018, Chef Software, Inc.
+#
+# 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.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class WindowsShortcut < Chef::Resource
+ resource_name :windows_shortcut
+ provides :windows_shortcut
+
+ description "Use the windows_shortcut resource to create shortcut files on Windows"
+ introduced "14.0"
+
+ property :shortcut_name, String,
+ description: "The name for the shortcut if it differs from the resource name.",
+ name_property: true
+
+ property :target, String,
+ description: "Where the shortcut links to."
+
+ property :arguments, String,
+ description: "Arguments to pass to the target when the shortcut is executed."
+
+ property :description, String,
+ description: "The description of the shortcut"
+
+ property :cwd, String,
+ description: "Working directory to use when the target is executed."
+
+ property :iconlocation, String,
+ description: "Icon to use for the shortcut, in the format of 'path, index'. Index is the icon file to use. See https://msdn.microsoft.com/en-us/library/3s9bx7at.aspx for details"
+
+ load_current_value do |desired|
+ require "win32ole" if RUBY_PLATFORM =~ /mswin|mingw32|windows/
+
+ link = WIN32OLE.new("WScript.Shell").CreateShortcut(desired.shortcut_name)
+ name desired.shortcut_name
+ target(link.TargetPath)
+ arguments(link.Arguments)
+ description(link.Description)
+ cwd(link.WorkingDirectory)
+ iconlocation(link.IconLocation)
+ end
+
+ action :create do
+ description "Create or modify a Windows shortcut."
+
+ converge_if_changed do
+ converge_by "creating shortcut #{new_resource.shortcut_name}" do
+ link = WIN32OLE.new("WScript.Shell").CreateShortcut(new_resource.shortcut_name)
+ link.TargetPath = new_resource.target unless new_resource.target.nil?
+ link.Arguments = new_resource.arguments unless new_resource.arguments.nil?
+ link.Description = new_resource.description unless new_resource.description.nil?
+ link.WorkingDirectory = new_resource.cwd unless new_resource.cwd.nil?
+ link.IconLocation = new_resource.iconlocation unless new_resource.iconlocation.nil?
+ # ignoring: WindowStyle, Hotkey
+ link.Save
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb
index 76a7a056e0..ba91660df0 100644
--- a/lib/chef/resources.rb
+++ b/lib/chef/resources.rb
@@ -109,5 +109,11 @@ require "chef/resource/zypper_repository"
require "chef/resource/cab_package"
require "chef/resource/powershell_package"
require "chef/resource/msu_package"
-require "chef/resource/windows_task"
+require "chef/resource/windows_auto_run"
+require "chef/resource/windows_font"
+require "chef/resource/windows_pagefile"
require "chef/resource/windows_path"
+require "chef/resource/windows_printer"
+require "chef/resource/windows_printer_port"
+require "chef/resource/windows_shortcut"
+require "chef/resource/windows_task"
diff --git a/spec/unit/resource/windows_auto_run_spec.rb b/spec/unit/resource/windows_auto_run_spec.rb
new file mode 100644
index 0000000000..c92bbb2f98
--- /dev/null
+++ b/spec/unit/resource/windows_auto_run_spec.rb
@@ -0,0 +1,50 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# 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.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::WindowsAutorun do
+ let(:resource) { Chef::Resource::WindowsAutorun.new("some_path") }
+
+ it "sets resource name as :windows_auto_run" do
+ expect(resource.resource_name).to eql(:windows_auto_run)
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "sets the program_name property as its name" do
+ expect(resource.program_name).to eql("some_path")
+ end
+ it "supports :machine and :user in the root property" do
+ expect { resource.root :user }.not_to raise_error
+ expect { resource.root :machine }.not_to raise_error
+ expect { resource.root "user" }.to raise_error(ArgumentError)
+ end
+
+ it "supports :create and :remove actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ expect { resource.action :delete }.to raise_error(ArgumentError)
+ end
+
+ it "coerces forward slashes to backslashes for the path" do
+ resource.path "C:/something.exe"
+ expect(resource.path).to eql('C:\\something.exe')
+ end
+end
diff --git a/spec/unit/resource/windows_font_spec.rb b/spec/unit/resource/windows_font_spec.rb
new file mode 100644
index 0000000000..a98e3f4a40
--- /dev/null
+++ b/spec/unit/resource/windows_font_spec.rb
@@ -0,0 +1,44 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# 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.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::WindowsFont do
+ let(:resource) { Chef::Resource::WindowsFont.new("some_font") }
+
+ it "sets resource name as :windows_font" do
+ expect(resource.resource_name).to eql(:windows_font)
+ end
+
+ it "sets the font_name as its name" do
+ expect(resource.font_name).to eql("some_font")
+ end
+
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "supports :install action" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :remove }.to raise_error(ArgumentError)
+ end
+
+ it "coerces backslashes in the source property to forward slashes" do
+ resource.source 'C:\foo\bar\fontfile'
+ expect(resource.source).to eql("C:/foo/bar/fontfile")
+ end
+end
diff --git a/spec/unit/resource/windows_pagefile_spec.rb b/spec/unit/resource/windows_pagefile_spec.rb
new file mode 100644
index 0000000000..8ff06bb3f0
--- /dev/null
+++ b/spec/unit/resource/windows_pagefile_spec.rb
@@ -0,0 +1,45 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# 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.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::WindowsPagefile do
+ let(:resource) { Chef::Resource::WindowsPagefile.new("C:/pagefile.sys") }
+
+ it "sets resource name as :windows_pagefile" do
+ expect(resource.resource_name).to eql(:windows_pagefile)
+ end
+
+ it "sets the path as its name" do
+ expect(resource.path).to eql("C:/pagefile.sys")
+ end
+
+ it "coerces backslashes in the path property to forward slashes" do
+ resource.path 'C:\pagefile.sys'
+ expect(resource.path).to eql("C:/pagefile.sys")
+ end
+
+ it "sets the default action as :set" do
+ expect(resource.action).to eql([:set])
+ end
+
+ it "supports :set and :delete actions" do
+ expect { resource.action :set }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :create }.to raise_error(ArgumentError)
+ end
+end
diff --git a/spec/unit/resource/windows_printer_port_spec.rb b/spec/unit/resource/windows_printer_port_spec.rb
new file mode 100644
index 0000000000..6b29c2610a
--- /dev/null
+++ b/spec/unit/resource/windows_printer_port_spec.rb
@@ -0,0 +1,45 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# 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.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::WindowsPrinterPort do
+ let(:resource) { Chef::Resource::WindowsPrinterPort.new("63.192.209.236") }
+
+ it "sets resource name as :windows_printer_port" do
+ expect(resource.resource_name).to eql(:windows_printer_port)
+ end
+
+ it "sets the ipv4_address as its name" do
+ expect(resource.ipv4_address).to eql("63.192.209.236")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports :create and :delete actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :remove }.to raise_error(ArgumentError)
+ end
+
+ it "raises an error if ipv4_address isn't in X.X.X.X format" do
+ expect { resource.ipv4_address "63.192.209.236" }.not_to raise_error
+ expect { resource.ipv4_address "a.b.c.d" }.to raise_error(ArgumentError)
+ end
+end
diff --git a/spec/unit/resource/windows_printer_spec.rb b/spec/unit/resource/windows_printer_spec.rb
new file mode 100644
index 0000000000..5c773005ea
--- /dev/null
+++ b/spec/unit/resource/windows_printer_spec.rb
@@ -0,0 +1,45 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# 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.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::WindowsPrinter do
+ let(:resource) { Chef::Resource::WindowsPrinter.new("some_printer") }
+
+ it "sets resource name as :windows_printer" do
+ expect(resource.resource_name).to eql(:windows_printer)
+ end
+
+ it "sets the device_id as its name" do
+ expect(resource.device_id).to eql("some_printer")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports :create and :delete actions" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :delete }.not_to raise_error
+ expect { resource.action :remove }.to raise_error(ArgumentError)
+ end
+
+ it "raises an error if ipv4_address isn't in X.X.X.X format" do
+ expect { resource.ipv4_address "63.192.209.236" }.not_to raise_error
+ expect { resource.ipv4_address "a.b.c.d" }.to raise_error(ArgumentError)
+ end
+end
diff --git a/spec/unit/resource/windows_shortcut_spec.rb b/spec/unit/resource/windows_shortcut_spec.rb
new file mode 100644
index 0000000000..0b5e2325b9
--- /dev/null
+++ b/spec/unit/resource/windows_shortcut_spec.rb
@@ -0,0 +1,39 @@
+#
+# Copyright:: Copyright 2018, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# 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.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::WindowsShortcut do
+ let(:resource) { Chef::Resource::WindowsShortcut.new("some_path") }
+
+ it "sets resource name as :windows_shortcut" do
+ expect(resource.resource_name).to eql(:windows_shortcut)
+ end
+
+ it "sets the shortcut_name property as its name" do
+ expect(resource.shortcut_name).to eql("some_path")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql([:create])
+ end
+
+ it "supports :create action only" do
+ expect { resource.action :create }.not_to raise_error
+ expect { resource.action :delete }.to raise_error(ArgumentError)
+ end
+end