Downgrade mysql cookbook for now

It doesn't play well with our current dev server setup
This commit is contained in:
Greg Karékinian
2017-06-16 22:43:51 +02:00
parent e39792ea36
commit bdfb3a1afb
398 changed files with 12716 additions and 10889 deletions

View File

@@ -1,30 +1,46 @@
#
# Author:: Paul Morton (<pmorton@biaprotect.com>)
# Cookbook Name:: windows
# Resource:: auto_run
#
# Copyright:: 2011, Business Intelligence Associates, 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.
#
def initialize(name, run_context = nil)
super
@action = :create
end
actions :create, :remove
attribute :program, kind_of: String
attribute :name, kind_of: String, name_attribute: true
attribute :args, kind_of: String, default: ''
#
# Author:: Paul Morton (<pmorton@biaprotect.com>)
# Cookbook:: windows
# Resource:: auto_run
#
# Copyright:: 2011-2017, Business Intelligence Associates, Inc.
# Copyright:: 2017, 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.
#
property :program, String
property :name, String, name_property: true
property :args, String
action :create do
registry_key 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run' do
values [{
name: new_resource.name,
type: :string,
data: "\"#{new_resource.program}\" #{new_resource.args}",
}]
action :create
end
end
action :remove do
registry_key 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run' do
values [{
name: new_resource.name,
type: :string,
data: '',
}]
action :delete
end
end

View File

@@ -1,41 +0,0 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
# Cookbook Name:: windows
# Resource:: batch
#
# Copyright:: 2011-2015, 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.
#
actions :run
attribute :command, kind_of: String, name_attribute: true
attribute :cwd, kind_of: String, default: nil
attribute :code, kind_of: String, default: nil
attribute :user, kind_of: [String, Integer], default: nil
attribute :group, kind_of: [String, Integer], default: nil
attribute :creates, kind_of: [String], default: nil
attribute :flags, kind_of: [String], default: nil
attribute :returns, kind_of: [Integer, Array], default: 0
def initialize(name, run_context = nil)
super
@action = :run
@command = name
Chef::Log.warn <<-EOF
Please use the batch resource in Chef Client 11 and 12.
windows_batch will be removed in the next major version release
of the Windows cookbook.
EOF
end

View File

@@ -1,28 +1,166 @@
#
# Author:: Richard Lavey (richard.lavey@calastone.com)
# Cookbook Name:: windows
# Resource:: certificate
#
# Copyright:: 2015, Calastone Ltd.
#
# 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.
#
actions :create, :delete, :acl_add
default_action :create
attribute :source, kind_of: String, name_attribute: true, required: true
attribute :pfx_password, kind_of: String
attribute :private_key_acl, kind_of: Array
attribute :store_name, kind_of: String, default: 'MY', regex: /^(?:MY|CA|ROOT)$/
attribute :user_store, kind_of: [TrueClass, FalseClass], default: false
#
# Author:: Richard Lavey (richard.lavey@calastone.com)
# Cookbook:: windows
# Resource:: certificate
#
# Copyright:: 2015-2017, Calastone Ltd.
#
# 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.
#
include Windows::Helper
property :source, String, name_property: true, required: true
property :pfx_password, String
property :private_key_acl, Array
property :store_name, String, default: 'MY', regex: /^(?:MY|CA|ROOT|TrustedPublisher|TRUSTEDPEOPLE)$/
property :user_store, [true, false], default: false
action :create do
hash = '$cert.GetCertHashString()'
code_script = cert_script(true) <<
within_store_script { |store| store + '.Add($cert)' } <<
acl_script(hash)
guard_script = cert_script(false) <<
cert_exists_script(hash)
converge_by("adding certificate #{new_resource.source} into #{new_resource.store_name} to #{cert_location}\\#{new_resource.store_name}") do
powershell_script new_resource.name do
guard_interpreter :powershell_script
convert_boolean_return true
code code_script
not_if guard_script
end
end
end
# acl_add is a modify-if-exists operation : not idempotent
action :acl_add do
if ::File.exist?(new_resource.source)
hash = '$cert.GetCertHashString()'
code_script = cert_script(false)
guard_script = cert_script(false)
else
# make sure we have no spaces in the hash string
hash = "\"#{new_resource.source.gsub(/\s/, '')}\""
code_script = ''
guard_script = ''
end
code_script << acl_script(hash)
guard_script << cert_exists_script(hash)
converge_by("setting the acls on #{new_resource.source} in #{cert_location}\\#{new_resource.store_name}") do
powershell_script new_resource.name do
guard_interpreter :powershell_script
convert_boolean_return true
code code_script
only_if guard_script
end
end
end
action :delete do
# do we have a hash or a subject?
# TODO: It's a bit annoying to know the thumbprint of a cert you want to remove when you already
# have the file. Support reading the hash directly from the file if provided.
search = if new_resource.source =~ /^[a-fA-F0-9]{40}$/
"Thumbprint -eq '#{new_resource.source}'"
else
"Subject -like '*#{new_resource.source.sub(/\*/, '`*')}*'" # escape any * in the source
end
cert_command = "Get-ChildItem Cert:\\#{cert_location}\\#{new_resource.store_name} | where { $_.#{search} }"
code_script = within_store_script do |store|
<<-EOH
foreach ($c in #{cert_command})
{
#{store}.Remove($c)
}
EOH
end
guard_script = "@(#{cert_command}).Count -gt 0\n"
converge_by("Removing certificate #{new_resource.source} from #{cert_location}\\#{new_resource.store_name}") do
powershell_script new_resource.name do
guard_interpreter :powershell_script
convert_boolean_return true
code code_script
only_if guard_script
end
end
end
action_class do
def cert_location
@location ||= new_resource.user_store ? 'CurrentUser' : 'LocalMachine'
end
def cert_script(persist)
cert_script = '$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2'
file = win_friendly_path(new_resource.source)
cert_script << " \"#{file}\""
if ::File.extname(file.downcase) == '.pfx'
cert_script << ", \"#{new_resource.pfx_password}\""
if persist && new_resource.user_store
cert_script << ', [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet'
elsif persist
cert_script << ', ([System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet -bor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeyset)'
end
end
cert_script << "\n"
end
def cert_exists_script(hash)
<<-EOH
$hash = #{hash}
Test-Path "Cert:\\#{cert_location}\\#{new_resource.store_name}\\$hash"
EOH
end
def within_store_script
inner_script = yield '$store'
<<-EOH
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store "#{new_resource.store_name}", ([System.Security.Cryptography.X509Certificates.StoreLocation]::#{cert_location})
$store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
#{inner_script}
$store.Close()
EOH
end
def acl_script(hash)
return '' if new_resource.private_key_acl.nil? || new_resource.private_key_acl.empty?
# this PS came from http://blogs.technet.com/b/operationsguy/archive/2010/11/29/provide-access-to-private-keys-commandline-vs-powershell.aspx
# and from https://msdn.microsoft.com/en-us/library/windows/desktop/bb204778(v=vs.85).aspx
set_acl_script = <<-EOH
$hash = #{hash}
$storeCert = Get-ChildItem "cert:\\#{cert_location}\\#{new_resource.store_name}\\$hash"
if ($storeCert -eq $null) { throw 'no key exists.' }
$keyname = $storeCert.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName
if ($keyname -eq $null) { throw 'no private key exists.' }
if ($storeCert.PrivateKey.CspKeyContainerInfo.MachineKeyStore)
{
$fullpath = "$Env:ProgramData\\Microsoft\\Crypto\\RSA\\MachineKeys\\$keyname"
}
else
{
$currentUser = New-Object System.Security.Principal.NTAccount($Env:UserDomain, $Env:UserName)
$userSID = $currentUser.Translate([System.Security.Principal.SecurityIdentifier]).Value
$fullpath = "$Env:ProgramData\\Microsoft\\Crypto\\RSA\\$userSID\\$keyname"
}
EOH
new_resource.private_key_acl.each do |name|
set_acl_script << "$uname='#{name}'; icacls $fullpath /grant $uname`:RX\n"
end
set_acl_script
end
end

View File

@@ -1,31 +1,128 @@
#
# Author:: Richard Lavey (richard.lavey@calastone.com)
# Cookbook Name:: windows
# Resource:: certificate_binding
#
# Copyright:: 2015, Calastone Ltd.
#
# 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.
#
actions :create, :delete
default_action :create
attribute :cert_name, kind_of: String, name_attribute: true, required: true
attribute :name_kind, kind_of: Symbol, equal_to: [:hash, :subject], default: :subject
attribute :address, kind_of: String, default: '0.0.0.0'
attribute :port, kind_of: Integer, default: 443
attribute :app_id, kind_of: String, default: '{4dc3e181-e14b-4a21-b022-59fc669b0914}'
attribute :store_name, kind_of: String, default: 'MY', regex: /^(?:MY|CA|ROOT)$/
attr_accessor :exists
#
# Author:: Richard Lavey (richard.lavey@calastone.com)
# Cookbook:: windows
# Resource:: certificate_binding
#
# Copyright:: 2015-2017, Calastone Ltd.
#
# 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.
#
include Chef::Mixin::ShellOut
include Chef::Mixin::PowershellOut
include Windows::Helper
property :cert_name, String, name_property: true, required: true
property :name_kind, Symbol, equal_to: [:hash, :subject], default: :subject
property :address, String, default: '0.0.0.0'
property :port, Integer, default: 443
property :app_id, String, default: '{4dc3e181-e14b-4a21-b022-59fc669b0914}'
property :store_name, String, default: 'MY', regex: /^(?:MY|CA|ROOT)$/
property :exists, [true, false], desired_state: true
load_current_value do |desired|
cmd = shell_out("#{locate_sysnative_cmd('netsh.exe')} http show sslcert ipport=#{desired.address}:#{desired.port}")
Chef::Log.debug "netsh reports: #{cmd.stdout}"
address desired.address
port desired.port
store_name desired.store_name
app_id desired.app_id
if cmd.exitstatus == 0
m = cmd.stdout.scan(/Certificate Hash\s+:\s?([A-Fa-f0-9]{40})/)
raise "Failed to extract hash from command output #{cmd.stdout}" if m.empty?
cert_name m[0][0]
name_kind :hash
exists true
else
exists false
end
end
action :create do
hash = new_resource.name_kind == :subject ? hash_from_subject : new_resource.cert_name
if current_resource.exists
needs_change = (hash.casecmp(current_resource.cert_name) != 0)
if needs_change
converge_by("Changing #{current_resource.address}:#{current_resource.port}") do
delete_binding
add_binding hash
end
else
Chef::Log.debug("#{new_resource.address}:#{new_resource.port} already bound to #{hash} - nothing to do")
end
else
converge_by("Binding #{new_resource.address}:#{new_resource.port}") do
add_binding hash
end
end
end
action :delete do
if current_resource.exists
converge_by("Deleting #{current_resource.address}:#{current_resource.port}") do
delete_binding
end
else
Chef::Log.debug("#{current_resource.address}:#{current_resource.port} not bound - nothing to do")
end
end
action_class do
def netsh_command
locate_sysnative_cmd('netsh.exe')
end
def add_binding(hash)
cmd = "#{netsh_command} http add sslcert"
cmd << " ipport=#{current_resource.address}:#{current_resource.port}"
cmd << " certhash=#{hash}"
cmd << " appid=#{current_resource.app_id}"
cmd << " certstorename=#{current_resource.store_name}"
check_hash hash
shell_out!(cmd)
end
def delete_binding
shell_out!("#{netsh_command} http delete sslcert ipport=#{current_resource.address}:#{current_resource.port}")
end
def check_hash(hash)
p = powershell_out!("Test-Path \"cert:\\LocalMachine\\#{current_resource.store_name}\\#{hash}\"")
unless p.stderr.empty? && p.stdout =~ /True/i
raise "A Cert with hash of #{hash} doesn't exist in keystore LocalMachine\\#{current_resource.store_name}"
end
nil
end
def hash_from_subject
# escape wildcard subject name (*.acme.com)
subject = new_resource.cert_name.sub(/\*/, '`*')
ps_script = "& { gci cert:\\localmachine\\#{new_resource.store_name} | where { $_.subject -like '*#{subject}*' } | select -first 1 -expandproperty Thumbprint }"
Chef::Log.debug "Running PS script #{ps_script}"
p = powershell_out!(ps_script)
raise "#{ps_script} failed with #{p.stderr}" if !p.stderr.nil? && !p.stderr.empty?
raise "Couldn't find thumbprint for subject #{new_resource.cert_name}" if p.stdout.nil? || p.stdout.empty?
# seem to get a UTF-8 string with BOM returned sometimes! Strip any such BOM
hash = p.stdout.strip
hash[0].ord == 239 ? hash.force_encoding('UTF-8').delete!("\xEF\xBB\xBF".force_encoding('UTF-8')) : hash
end
end

View File

@@ -1,47 +1,82 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
# Cookbook Name:: windows
# Resource:: feature
#
# Copyright:: 2011-2015, 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.
#
include Windows::Helper
actions :install, :remove, :delete
attribute :feature_name, kind_of: String, name_attribute: true
attribute :source, kind_of: String
attribute :all, kind_of: [TrueClass, FalseClass], default: false
def initialize(name, run_context = nil)
super
@action = :install
@provider = lookup_provider_constant(locate_default_provider)
end
private
def locate_default_provider
if node['windows'].attribute?(:feature_provider)
"windows_feature_#{node['windows']['feature_provider']}"
elsif ::File.exist?(locate_sysnative_cmd('dism.exe'))
:windows_feature_dism
elsif ::File.exist?(locate_sysnative_cmd('servermanagercmd.exe'))
:windows_feature_servermanagercmd
else
:windows_feature_powershell
end
end
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
# Cookbook:: windows
# Resource:: feature
#
# Copyright:: 2011-2017, 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.
#
property :feature_name, [Array, String], name_property: true
property :source, String
property :all, [true, false], default: false
property :install_method, Symbol, equal_to: [:windows_feature_dism, :windows_feature_powershell, :windows_feature_servermanagercmd]
include Windows::Helper
def whyrun_supported?
true
end
action :install do
run_default_provider :install
end
action :remove do
run_default_provider :remove
end
action :delete do
run_default_provider :delete
end
action_class do
def locate_default_provider
if new_resource.install_method
new_resource.install_method
elsif ::File.exist?(locate_sysnative_cmd('dism.exe'))
:windows_feature_dism
elsif ::File.exist?(locate_sysnative_cmd('servermanagercmd.exe'))
:windows_feature_servermanagercmd
else
:windows_feature_powershell
end
end
def run_default_provider(desired_action)
case locate_default_provider
when :windows_feature_dism
windows_feature_dism new_resource.name do
action desired_action
feature_name new_resource.feature_name
source new_resource.source if new_resource.source
all new_resource.all
end
when :windows_feature_servermanagercmd
windows_feature_servermanagercmd new_resource.name do
action desired_action
feature_name new_resource.feature_name
source new_resource.source if new_resource.source
all new_resource.all
end
when :windows_feature_powershell
windows_feature_powershell new_resource.name do
action desired_action
feature_name new_resource.feature_name
source new_resource.source if new_resource.source
all new_resource.all
end
end
end
end

View File

@@ -0,0 +1,108 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
# Cookbook:: windows
# Provider:: feature_dism
#
# Copyright:: 2011-2017, 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.
#
property :feature_name, [Array, String], name_property: true
property :source, String
property :all, [true, false], default: false
include Chef::Mixin::ShellOut
include Windows::Helper
action :install do
Chef::Log.warn("Requested feature #{new_resource.feature_name} is not available on this system.") unless available?
unless !available? || installed?
converge_by("install Windows feature #{new_resource.feature_name}") do
addsource = new_resource.source ? "/LimitAccess /Source:\"#{new_resource.source}\"" : ''
addall = new_resource.all ? '/All' : ''
shell_out!("#{dism} /online /enable-feature #{to_array(new_resource.feature_name).map { |feature| "/featurename:#{feature}" }.join(' ')} /norestart #{addsource} #{addall}", returns: [0, 42, 127, 3010])
# Reload ohai data
reload_ohai_features_plugin(new_resource.action, new_resource.feature_name)
end
end
end
action :remove do
if installed?
converge_by("removing Windows feature #{new_resource.feature_name}") do
shell_out!("#{dism} /online /disable-feature #{to_array(new_resource.feature_name).map { |feature| "/featurename:#{feature}" }.join(' ')} /norestart", returns: [0, 42, 127, 3010])
# Reload ohai data
reload_ohai_features_plugin(new_resource.action, new_resource.feature_name)
end
end
end
action :delete do
raise Chef::Exceptions::UnsupportedAction, "#{self} :delete action not support on #{win_version.sku}" unless supports_feature_delete?
if available?
converge_by("deleting Windows feature #{new_resource.feature_name} from the image") do
shell_out!("#{dism} /online /disable-feature #{to_array(new_resource.feature_name).map { |feature| "/featurename:#{feature}" }.join(' ')} /Remove /norestart", returns: [0, 42, 127, 3010])
# Reload ohai data
reload_ohai_features_plugin(new_resource.action, new_resource.feature_name)
end
end
end
action_class do
def installed?
@installed ||= begin
install_ohai_plugin unless node['dism_features']
# Compare against ohai plugin instead of costly dism run
node['dism_features'].key?(new_resource.feature_name) && node['dism_features'][new_resource.feature_name] =~ /Enable/
end
end
def available?
@available ||= begin
install_ohai_plugin unless node['dism_features']
# Compare against ohai plugin instead of costly dism run
node['dism_features'].key?(new_resource.feature_name) && node['dism_features'][new_resource.feature_name] !~ /with payload removed/
end
end
def reload_ohai_features_plugin(take_action, feature_name)
ohai "Reloading Dism_Features Plugin - Action #{take_action} of feature #{feature_name}" do
action :reload
plugin 'dism_features'
end
end
def install_ohai_plugin
Chef::Log.info("node['dism_features'] data missing. Installing the dism_features Ohai plugin")
ohai_plugin 'dism_features' do
compile_time true
cookbook 'windows'
end
end
def supports_feature_delete?
win_version.major_version >= 6 && win_version.minor_version >= 2
end
# account for File System Redirector
# http://msdn.microsoft.com/en-us/library/aa384187(v=vs.85).aspx
def dism
@dism ||= begin
locate_sysnative_cmd('dism.exe')
end
end
end

View File

@@ -0,0 +1,70 @@
#
# Author:: Greg Zapp (<greg.zapp@gmail.com>)
# Cookbook:: windows
# Provider:: feature_powershell
#
property :feature_name, [Array, String], name_attribute: true
property :source, String
property :all, [true, false], default: false
include Chef::Mixin::PowershellOut
include Windows::Helper
action :remove do
if installed?
converge_by("remove Windows feature #{new_resource.feature_name}") do
cmd = powershell_out!("#{remove_feature_cmdlet} #{to_array(new_resource.feature_name).join(',')}")
Chef::Log.info(cmd.stdout)
end
end
end
action :delete do
if available?
converge_by("delete Windows feature #{new_resource.feature_name} from the image") do
cmd = powershell_out!("Uninstall-WindowsFeature #{to_array(new_resource.feature_name).join(',')} -Remove")
Chef::Log.info(cmd.stdout)
end
end
end
action_class do
def install_feature_cmdlet
node['os_version'].to_f < 6.2 ? 'Import-Module ServerManager; Add-WindowsFeature' : 'Install-WindowsFeature'
end
def remove_feature_cmdlet
node['os_version'].to_f < 6.2 ? 'Import-Module ServerManager; Remove-WindowsFeature' : 'Uninstall-WindowsFeature'
end
def installed?
@installed ||= begin
cmd = powershell_out("(Get-WindowsFeature #{to_array(new_resource.feature_name).join(',')} | ?{$_.InstallState -ne \'Installed\'}).count")
cmd.stderr.empty? && cmd.stdout.chomp.to_i == 0
end
end
def available?
@available ||= begin
cmd = powershell_out("(Get-WindowsFeature #{to_array(new_resource.feature_name).join(',')} | ?{$_.InstallState -ne \'Removed\'}).count")
cmd.stderr.empty? && cmd.stdout.chomp.to_i > 0
end
end
end
action :install do
Chef::Log.warn("Requested feature #{new_resource.feature_name} is not available on this system.") unless available?
unless !available? || installed?
converge_by("install Windows feature #{new_resource.feature_name}") do
addsource = new_resource.source ? "-Source \"#{new_resource.source}\"" : ''
addall = new_resource.all ? '-IncludeAllSubFeature' : ''
cmd = if node['os_version'].to_f < 6.2
powershell_out!("#{install_feature_cmdlet} #{to_array(new_resource.feature_name).join(',')} #{addall}")
else
powershell_out!("#{install_feature_cmdlet} #{to_array(new_resource.feature_name).join(',')} #{addsource} #{addall}")
end
Chef::Log.info(cmd.stdout)
end
end
end

View File

@@ -0,0 +1,76 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
# Cookbook:: windows
# Provider:: feature_servermanagercmd
#
# Copyright:: 2011-2017, 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.
#
property :feature_name, [Array, String], name_attribute: true
property :source, String
property :all, [true, false], default: false
include Chef::Mixin::ShellOut
include Windows::Helper
action :install do
unless installed?
converge_by("install Windows feature #{new_resource.feature_name}") do
check_reboot(shell_out("#{servermanagercmd} -install #{to_array(new_resource.feature_name).join(' ')}", returns: [0, 42, 127, 1003, 3010]), new_resource.feature_name)
end
end
end
action :remove do
if installed?
converge_by("removing Windows feature #{new_resource.feature_name}") do
check_reboot(shell_out("#{servermanagercmd} -remove #{to_array(new_resource.feature_name).join(' ')}", returns: [0, 42, 127, 1003, 3010]), new_resource.feature_name)
end
end
end
action :delete do
Chef::Log.warn('servermanagercmd does not support removing a feature from the image.')
end
# Exit codes are listed at http://technet.microsoft.com/en-us/library/cc749128(v=ws.10).aspx
action_class do
def check_reboot(result, feature)
if result.exitstatus == 3010 # successful, but needs reboot
node.run_state['reboot_requested'] = true
Chef::Log.warn("Require reboot to install #{feature}")
elsif result.exitstatus == 1001 # failure, but needs reboot before we can do anything else
node.run_state['reboot_requested'] = true
Chef::Log.warn("Failed installing #{feature} and need to reboot")
end
result.error! # throw for any other bad results. The above results will also get raised, and should cause a reboot via the handler.
end
def installed?
@installed ||= begin
cmd = shell_out("#{servermanagercmd} -query", returns: [0, 42, 127, 1003])
cmd.stderr.empty? && (cmd.stdout =~ /^\s*?\[X\]\s.+?\s\[#{new_resource.feature_name}\]\s*$/i)
end
end
# account for File System Redirector
# http://msdn.microsoft.com/en-us/library/aa384187(v=vs.85).aspx
def servermanagercmd
@servermanagercmd ||= begin
locate_sysnative_cmd('servermanagercmd.exe')
end
end
end

View File

@@ -1,25 +1,80 @@
#
# Author:: Sander Botman <sbotman@schubergphilis.com>
# Cookbook Name:: windows
# Resource:: font
#
# Copyright:: 2014, Schuberg Philis BV.
#
# 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.
#
actions :install
default_action :install
attribute :file, kind_of: String, name_attribute: true
#
# Author:: Sander Botman <sbotman@schubergphilis.com>
# Cookbook:: windows
# Resource:: font
#
# Copyright:: 2014-2017, Schuberg Philis BV.
# Copyright:: 2017, 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.
#
property :name, String, name_property: true
property :source, String, required: false
include Windows::Helper
action :install do
if font_exists?
Chef::Log.debug("Not installing font: #{new_resource.name}, font already installed.")
else
retrieve_cookbook_font
install_font
del_cookbook_font
end
end
action_class do
def retrieve_cookbook_font
font_file = new_resource.name
if new_resource.source
remote_file font_file do
action :nothing
source "file://#{new_resource.source}"
path win_friendly_path(::File.join(ENV['TEMP'], font_file))
end.run_action(:create)
else
cookbook_file font_file do
action :nothing
cookbook cookbook_name.to_s unless cookbook_name.nil?
path win_friendly_path(::File.join(ENV['TEMP'], font_file))
end.run_action(:create)
end
end
def del_cookbook_font
file ::File.join(ENV['TEMP'], new_resource.name) do
action :delete
end
end
def install_font
require 'win32ole' if RUBY_PLATFORM =~ /mswin|mingw32|windows/
fonts_dir = WIN32OLE.new('WScript.Shell').SpecialFolders('Fonts')
folder = WIN32OLE.new('Shell.Application').Namespace(fonts_dir)
converge_by("install font #{new_resource.name}") do
folder.CopyHere(win_friendly_path(::File.join(ENV['TEMP'], new_resource.name)))
end
end
# Check to see if the font is installed
#
# === Returns
# <true>:: If the font is installed
# <false>:: If the font is not instaled
def font_exists?
require 'win32ole' if RUBY_PLATFORM =~ /mswin|mingw32|windows/
fonts_dir = WIN32OLE.new('WScript.Shell').SpecialFolders('Fonts')
::File.exist?(win_friendly_path(::File.join(fonts_dir, new_resource.name)))
end
end

View File

@@ -1,27 +1,110 @@
#
# Author:: Richard Lavey (richard.lavey@calastone.com)
# Cookbook Name:: windows
# Resource:: http_acl
#
# Copyright:: 2015, Calastone Ltd.
#
# 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.
#
actions :create, :delete
default_action :create
attribute :url, kind_of: String, name_attribute: true, required: true
attribute :user, kind_of: String
attr_accessor :exists
#
# Author:: Richard Lavey (richard.lavey@calastone.com)
# Cookbook:: windows
# Resource:: http_acl
#
# Copyright:: 2015-2017, Calastone Ltd.
#
# 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.
#
include Chef::Mixin::ShellOut
include Windows::Helper
property :url, String, name_property: true, required: true
property :user, String
property :sddl, String
property :exists, [true, false], desired_state: true
# See https://msdn.microsoft.com/en-us/library/windows/desktop/cc307236%28v=vs.85%29.aspx for netsh info
load_current_value do |desired|
cmd_out = shell_out!("#{locate_sysnative_cmd('netsh.exe')} http show urlacl url=#{desired.url}").stdout
Chef::Log.debug "netsh reports: #{cmd_out}"
if cmd_out.include? desired.url
exists true
url desired.url
# Checks first for sddl, because it generates user(s)
sddl_match = cmd_out.match(/SDDL:\s*(?<sddl>.+)/)
if sddl_match
sddl sddl_match['sddl']
else
# if no sddl, tries to find a single user
user_match = cmd_out.match(/User:\s*(?<user>.+)/)
user user_match['user']
end
else
exists false
end
end
action :create do
raise '`user` xor `sddl` can\'t be used together' if new_resource.user && new_resource.sddl
raise 'When provided user property can\'t be empty' if new_resource.user && new_resource.user.empty?
raise 'When provided sddl property can\'t be empty' if new_resource.sddl && new_resource.sddl.empty?
if current_resource.exists
sddl_changed = (
new_resource.sddl &&
current_resource.sddl &&
current_resource.sddl.casecmp(new_resource.sddl) != 0
)
user_changed = (
new_resource.user &&
current_resource.user &&
current_resource.user.casecmp(new_resource.user) != 0
)
if sddl_changed || user_changed
converge_by("Changing #{new_resource.url}") do
delete_acl
apply_acl
end
else
Chef::Log.debug("#{new_resource.url} already set - nothing to do")
end
else
converge_by("Setting #{new_resource.url}") do
apply_acl
end
end
end
action :delete do
if current_resource.exists
converge_by("Deleting #{new_resource.url}") do
delete_acl
end
else
Chef::Log.debug("#{new_resource.url} does not exist - nothing to do")
end
end
action_class do
def netsh_command
locate_sysnative_cmd('netsh.exe')
end
def apply_acl
if current_resource.sddl
shell_out!("#{netsh_command} http add urlacl url=#{new_resource.url} sddl=\"#{new_resource.sddl}\"")
else
shell_out!("#{netsh_command} http add urlacl url=#{new_resource.url} user=\"#{new_resource.user}\"")
end
end
def delete_acl
shell_out!("#{netsh_command} http delete urlacl url=#{new_resource.url}")
end
end

View File

@@ -1,29 +1,156 @@
#
# Author:: Kevin Moser (<kevin.moser@nordstrom.com>)
# Cookbook Name:: windows
# Resource:: pagefile
#
# Copyright:: 2012, 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.
#
actions :set, :delete
attribute :name, kind_of: String, name_attribute: true
attribute :system_managed, kind_of: [TrueClass, FalseClass]
attribute :automatic_managed, kind_of: [TrueClass, FalseClass], default: false
attribute :initial_size, kind_of: Integer
attribute :maximum_size, kind_of: Integer
default_action :set
#
# Author:: Kevin Moser (<kevin.moser@nordstrom.com>)
# Cookbook:: windows
# Resource:: pagefile
#
# Copyright:: 2012-2017, Nordstrom, Inc.
# Copyright:: 2017, 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.
#
property :name, String, name_property: true
property :system_managed, [true, false]
property :automatic_managed, [true, false], default: false
property :initial_size, Integer
property :maximum_size, Integer
include Chef::Mixin::ShellOut
include Windows::Helper
action :set do
pagefile = new_resource.name
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
validate_name
pagefile = new_resource.name
delete(pagefile) if exists?(pagefile)
end
action_class do
def validate_name
return if /^.:.*.sys/ =~ new_resource.name
raise "#{new_resource.name} does not match the format DRIVE:\\path\\file.sys for pagefiles. Example: C:\\pagefile.sys"
end
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
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
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
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
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
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
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
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
def set_system_managed(pagefile) # rubocop: disable Style/AccessorMethodName
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
def check_for_errors(stderr)
raise stderr.chomp unless stderr.empty?
end
def wmic
@wmic ||= locate_sysnative_cmd('wmic.exe')
end
end

View File

@@ -1,28 +1,54 @@
#
# Author:: Paul Morton (<pmorton@biaprotect.com>)
# Cookbook Name:: windows
# Resource:: path
#
# Copyright:: 2011, Business Intelligence Associates, 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.
#
def initialize(name, run_context = nil)
super
@action = :add
end
actions :add, :remove
attribute :path, kind_of: String, name_attribute: true
#
# Author:: Paul Morton (<pmorton@biaprotect.com>)
# Cookbook:: windows
# Resource:: path
#
# Copyright:: 2011-2017, Business Intelligence Associates, Inc
# Copyright:: 2017, 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.
#
property :path, String, name_property: true
include Windows::Helper
action :add do
env 'path' do
action :modify
delim ::File::PATH_SEPARATOR
value new_resource.path.tr('/', '\\')
notifies :run, "ruby_block[fix ruby ENV['PATH']]", :immediately
end
# The windows Env provider does not correctly expand variables in
# the PATH environment variable. Ruby expects these to be expanded.
# This is a temporary fix for that.
#
# Follow at https://github.com/chef/chef/pull/1876
#
ruby_block "fix ruby ENV['PATH']" do
block do
ENV['PATH'] = expand_env_vars(ENV['PATH'])
end
action :nothing
end
end
action :remove do
env 'path' do
action :delete
delim ::File::PATH_SEPARATOR
value new_resource.path.tr('/', '\\')
end
end

View File

@@ -1,41 +1,103 @@
#
# Author:: Doug Ireton (<doug.ireton@nordstrom.com>)
# Cookbook Name:: windows
# Resource:: printer
#
# Copyright:: 2012, 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 'resolv'
actions :create, :delete
default_action :create
attribute :device_id, kind_of: String, name_attribute: true,
required: true
attribute :comment, kind_of: String
attribute :default, kind_of: [TrueClass, FalseClass], default: false
attribute :driver_name, kind_of: String, required: true
attribute :location, kind_of: String
attribute :shared, kind_of: [TrueClass, FalseClass], default: false
attribute :share_name, kind_of: String
attribute :ipv4_address, kind_of: String, regex: Resolv::IPv4::Regex
attr_accessor :exists
#
# Author:: Doug Ireton (<doug.ireton@nordstrom.com>)
# Cookbook:: windows
# Resource:: printer
#
# Copyright:: 2012-2017, 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 'resolv'
property :device_id, String, name_property: true, required: true
property :comment, String
property :default, [true, false], default: false
property :driver_name, String, required: true
property :location, String
property :shared, [true, false], default: false
property :share_name, String
property :ipv4_address, String, 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)
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
load_current_value do |desired|
name desired.name
exists printer_exists?(desired.name)
# TODO: Set @current_resource printer properties from registry
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
end
end
end
action :delete do
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
def create_printer
# Create the printer port first
windows_printer_port new_resource.ipv4_address do
end
port_name = "IP_#{new_resource.ipv4_address}"
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
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

View File

@@ -1,40 +1,101 @@
#
# Author:: Doug Ireton (<doug.ireton@nordstrom.com>)
# Cookbook Name:: windows
# Resource:: printer_port
#
# Copyright:: 2012, 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 'resolv'
actions :create, :delete
default_action :create
attribute :ipv4_address, name_attribute: true, kind_of: String,
required: true, regex: Resolv::IPv4::Regex
attribute :port_name, kind_of: String
attribute :port_number, kind_of: Fixnum, default: 9100
attribute :port_description, kind_of: String
attribute :snmp_enabled, kind_of: [TrueClass, FalseClass],
default: false
attribute :port_protocol, kind_of: Fixnum, default: 1, equal_to: [1, 2]
attr_accessor :exists
#
# Author:: Doug Ireton (<doug.ireton@nordstrom.com>)
# Cookbook:: windows
# Resource:: printer_port
#
# Copyright:: 2012-2017, 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 'resolv'
property :ipv4_address, String, name_attribute: true, required: true, regex: Resolv::IPv4::Regex
property :port_name, String
property :port_number, Integer, default: 9100
property :port_description, String
property :snmp_enabled, [true, false], default: false
property :port_protocol, Integer, 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
load_current_value do |desired|
name desired.name
ipv4_address desired.ipv4_address
port_name desired.port_name || "IP_#{@new_resource.ipv4_address}"
exists port_exists?(desired.port_name)
# TODO: Set @current_resource port properties from registry
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
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}"
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

View File

@@ -1,34 +0,0 @@
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
# Cookbook Name:: windows
# Resource:: reboot
#
# Copyright:: 2011-2015, 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.
#
actions :request, :cancel
attribute :timeout, kind_of: Integer, name_attribute: true
attribute :reason, kind_of: String, default: 'Chef client run'
def initialize(name, run_context = nil)
super
@action = :request
Chef::Log.warn <<-EOF
The windows_reboot resource is deprecated. Please use the reboot resource in
Chef Client 12. windows_reboot will be removed in the next major version
release of the Windows cookbook.
EOF
end

View File

@@ -1,38 +0,0 @@
#
# Author:: Doug MacEachern (<dougm@vmware.com>)
# Author:: Seth Chisamore (<schisamo@chef.io>)
# Cookbook Name:: windows
# Resource:: registry
#
# Copyright:: 2010, VMware, Inc.
# Copyright:: 2011-2015, 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.
#
actions :create, :modify, :force_modify, :remove
attribute :key_name, kind_of: String, name_attribute: true
attribute :values, kind_of: Hash
attribute :type, kind_of: Symbol, default: nil, equal_to: [:binary, :string, :multi_string, :expand_string, :dword, :dword_big_endian, :qword]
def initialize(name, run_context = nil)
super
@action = :modify
@key_name = name
Chef::Log.warn <<-EOF
Please use the registry_key resource in Chef Client 11 and 12.
windows_registry will be removed in the next major version release
of the Windows cookbook.
EOF
end

View File

@@ -0,0 +1,289 @@
# -*- coding: utf-8 -*-
#
# Author:: Sölvi Páll Ásgeirsson (<solvip@gmail.com>), Richard Lavey (richard.lavey@calastone.com)
# Cookbook:: windows
# Resource:: share
#
# Copyright:: 2014-2017, Sölvi Páll Ásgeirsson.
#
# 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.
#
property :share_name, String, name_property: true
property :path, String, required: true
property :description, String, default: ''
property :full_users, Array, default: []
property :change_users, Array, default: []
property :read_users, Array, default: []
include Windows::Helper
include Chef::Mixin::PowershellOut
require 'win32ole' if RUBY_PLATFORM =~ /mswin|mingw32|windows/
ACCESS_FULL = 2_032_127
ACCESS_CHANGE = 1_245_631
ACCESS_READ = 1_179_817
action :create do
if different_path?
unless current_resource.path.nil? || current_resource.path.empty?
converge_by('Removing previous share') do
delete_share
end
end
converge_by("Creating share #{current_resource.share_name}") do
create_share
end
end
if different_members?(:full_users) ||
different_members?(:change_users) ||
different_members?(:read_users) ||
different_description?
converge_by("Setting permissions and description for #{new_resource.share_name}") do
set_share_permissions
end
end
end
action :delete do
if !current_resource.path.nil? && !current_resource.path.empty?
converge_by("Deleting #{current_resource.share_name}") do
delete_share
end
else
Chef::Log.debug("#{current_resource.share_name} does not exist - nothing to do")
end
end
load_current_value do |desired|
wmi = WIN32OLE.connect('winmgmts://')
shares = wmi.ExecQuery("SELECT * FROM Win32_Share WHERE name = '#{desired.share_name}'")
existing_share = shares.Count == 0 ? nil : shares.ItemIndex(0)
description ''
unless existing_share.nil?
path existing_share.Path
description existing_share.Description
end
perms = share_permissions name
unless perms.nil?
full_users perms[:full_users]
change_users perms[:change_users]
read_users perms[:read_users]
end
end
def share_permissions(name)
wmi = WIN32OLE.connect('winmgmts://')
shares = wmi.ExecQuery("SELECT * FROM Win32_LogicalShareSecuritySetting WHERE name = '#{name}'")
# The security descriptor is an output parameter
sd = nil
begin
shares.ItemIndex(0).GetSecurityDescriptor(sd)
sd = WIN32OLE::ARGV[0]
rescue WIN32OLERuntimeError
Chef::Log.warn('Failed to retrieve any security information about the share.')
end
read = []
change = []
full = []
unless sd.nil?
sd.DACL.each do |dacl|
trustee = "#{dacl.Trustee.Domain}\\#{dacl.Trustee.Name}".downcase
case dacl.AccessMask
when ACCESS_FULL
full.push(trustee)
when ACCESS_CHANGE
change.push(trustee)
when ACCESS_READ
read.push(trustee)
else
Chef::Log.warn "Unknown access mask #{dacl.AccessMask} for user #{trustee}. This will be lost if permissions are updated"
end
end
end
{
full_users: full,
change_users: change,
read_users: read,
}
end
action_class do
def description_exists?(resource)
!resource.description.nil?
end
def different_description?
if description_exists?(new_resource) && description_exists?(current_resource)
new_resource.description.casecmp(current_resource.description) != 0
else
description_exists?(new_resource) || description_exists?(current_resource)
end
end
def different_path?
return true if current_resource.path.nil?
win_friendly_path(new_resource.path).casecmp(win_friendly_path(current_resource.path)) != 0
end
def different_members?(permission_type)
!(current_resource.send(permission_type.to_sym) - new_resource.send(permission_type.to_sym).map(&:downcase)).empty? &&
!(new_resource.send(permission_type.to_sym).map(&:downcase) - current_resource.send(permission_type.to_sym)).empty?
end
def find_share_by_name(name)
wmi = WIN32OLE.connect('winmgmts://')
shares = wmi.ExecQuery("SELECT * FROM Win32_Share WHERE name = '#{name}'")
shares.Count == 0 ? nil : shares.ItemIndex(0)
end
def delete_share
find_share_by_name(new_resource.share_name).delete
end
def create_share
raise "#{new_resource.path} is missing or not a directory" unless ::File.directory? new_resource.path
new_share_script = <<-EOH
$share = [wmiclass]"\\\\#{ENV['COMPUTERNAME']}\\root\\CimV2:Win32_Share"
$result=$share.Create('#{new_resource.path}',
'#{new_resource.share_name}',
0,
16777216,
'#{new_resource.description}',
$null,
$null)
exit $result.returnValue
EOH
r = powershell_out new_share_script
message = case r.exitstatus
when 2
'2 : Access Denied'
when 8
'8 : Unknown Failure'
when 9
'9 : Invalid Name'
when 10
'10 : Invalid Level'
when 21
'21 : Invalid Parameter'
when 22
'22 : Duplicate Share'
when 23
'23 : Redirected Path'
when 24
'24 : Unknown Device or Directory'
when 25
'25 : Net Name Not Found'
else
r.exitstatus.to_s
end
raise "Could not create share. Win32_Share.create returned #{message}" if r.error?
end
# set_share_permissions - Enforce the share permissions as dictated by the resource attributes
def set_share_permissions
share_permissions_script = <<-EOH
Function New-SecurityDescriptor
{
param (
[array]$ACEs
)
#Create SeCDesc object
$SecDesc = ([WMIClass] "\\\\$env:ComputerName\\root\\cimv2:Win32_SecurityDescriptor").CreateInstance()
foreach ($ACE in $ACEs )
{
$SecDesc.DACL += $ACE.psobject.baseobject
}
#Return the security Descriptor
return $SecDesc
}
Function New-ACE
{
param (
[string] $Name,
[string] $Domain,
[string] $Permission = "Read"
)
#Create the Trusteee Object
$Trustee = ([WMIClass] "\\\\$env:computername\\root\\cimv2:Win32_Trustee").CreateInstance()
$account = get-wmiobject Win32_Account -filter "Name like '$Name' and Domain like '$Domain'"
$accountSID = [WMI] "\\\\$env:ComputerName\\root\\cimv2:Win32_SID.SID='$($account.sid)'"
$Trustee.Domain = $Domain
$Trustee.Name = $Name
$Trustee.SID = $accountSID.BinaryRepresentation
#Create ACE (Access Control List) object.
$ACE = ([WMIClass] "\\\\$env:ComputerName\\root\\cimv2:Win32_ACE").CreateInstance()
switch ($Permission)
{
"Read" { $ACE.AccessMask = 1179817 }
"Change" { $ACE.AccessMask = 1245631 }
"Full" { $ACE.AccessMask = 2032127 }
default { throw "$Permission is not a supported permission value. Possible values are 'Read','Change','Full'" }
}
$ACE.AceFlags = 3
$ACE.AceType = 0
$ACE.Trustee = $Trustee
$ACE
}
$dacl_array = @()
EOH
new_resource.full_users.each do |user|
share_permissions_script += user_to_ace(user, 'Full')
end
new_resource.change_users.each do |user|
share_permissions_script += user_to_ace(user, 'Change')
end
new_resource.read_users.each do |user|
share_permissions_script += user_to_ace(user, 'Read')
end
share_permissions_script += <<-EOH
$dacl = New-SecurityDescriptor -Aces $dacl_array
$share = get-wmiobject win32_share -filter 'Name like "#{new_resource.share_name}"'
$return = $share.SetShareInfo($null, '#{new_resource.description}', $dacl)
exit $return.returnValue
EOH
r = powershell_out(share_permissions_script)
raise "Could not set share permissions. Win32_Share.SedtShareInfo returned #{r.exitstatus}" if r.error?
end
def user_to_ace(fully_qualified_user_name, access)
domain, user = fully_qualified_user_name.split('\\')
unless domain && user
raise "Invalid user entry #{fully_qualified_user_name}. The user names must be specified as 'DOMAIN\\user'"
end
"\n$dacl_array += new-ace -Name '#{user}' -domain '#{domain}' -permission '#{access}'"
end
end

View File

@@ -1,36 +1,53 @@
#
# Author:: Doug MacEachern <dougm@vmware.com>
# Cookbook Name:: windows
# Resource:: shortcut
#
# Copyright:: 2010, VMware, 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.
#
actions :create
default_action :create
attribute :name, kind_of: String
attribute :target, kind_of: String
attribute :arguments, kind_of: String
attribute :description, kind_of: String
attribute :cwd, kind_of: String
attribute :iconlocation, kind_of: String
# Covers 0.10.8 and earlier
def initialize(*args)
super
@action = :create
end
#
# Author:: Doug MacEachern <dougm@vmware.com>
# Cookbook:: windows
# Resource:: shortcut
#
# Copyright:: 2010-2017, VMware, 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.
#
property :name, String
property :target, String
property :arguments, String
property :description, String
property :cwd, String
property :iconlocation, String
load_current_value do |desired|
require 'win32ole' if RUBY_PLATFORM =~ /mswin|mingw32|windows/
link = WIN32OLE.new('WScript.Shell').CreateShortcut(desired.name)
name desired.name
target(link.TargetPath)
arguments(link.Arguments)
description(link.Description)
cwd(link.WorkingDirectory)
iconlocation(link.IconLocation)
end
action :create do
converge_if_changed do
converge_by "creating shortcut #{new_resource.name}" do
link = WIN32OLE.new('WScript.Shell').CreateShortcut(new_resource.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

View File

@@ -1,52 +1,384 @@
#
# Author:: Paul Mooring (<paul@chef.io>)
# Cookbook Name:: windows
# Resource:: task
#
# Copyright:: 2012-2015, 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.
#
# Passwords can't be loaded for existing tasks, making :modify both confusing
# and not very useful
actions :create, :delete, :run, :end, :change, :enable, :disable
attribute :task_name, kind_of: String, name_attribute: true, regex: [/\A[^\/\:\*\?\<\>\|]+\z/]
attribute :command, kind_of: String
attribute :cwd, kind_of: String
attribute :user, kind_of: String, default: 'SYSTEM'
attribute :password, kind_of: String, default: nil
attribute :run_level, equal_to: [:highest, :limited], default: :limited
attribute :force, kind_of: [TrueClass, FalseClass], default: false
attribute :interactive_enabled, kind_of: [TrueClass, FalseClass], default: false
attribute :frequency_modifier, kind_of: Integer, default: 1
attribute :frequency, equal_to: [:minute,
:hourly,
:daily,
:weekly,
:monthly,
:once,
:on_logon,
:onstart,
:on_idle], default: :hourly
attribute :start_day, kind_of: String, default: nil
attribute :start_time, kind_of: String, default: nil
attribute :day, kind_of: [String, Integer], default: nil
attr_accessor :exists, :status, :enabled
def initialize(name, run_context = nil)
super
@action = :create
end
#
# Author:: Paul Mooring (<paul@chef.io>)
# Cookbook:: windows
# Resource:: task
#
# Copyright:: 2012-2017, 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.
#
# Passwords can't be loaded for existing tasks, making :modify both confusing
# and not very useful
require 'chef/mixin/shell_out'
require 'rexml/document'
include Chef::Mixin::ShellOut
include Chef::Mixin::PowershellOut
property :task_name, String, name_property: true, regex: [/\A[^\/\:\*\?\<\>\|]+\z/]
property :command, String
property :cwd, String
property :user, String, default: 'SYSTEM'
property :password, String
property :run_level, equal_to: [:highest, :limited], default: :limited
property :force, [true, false], default: false
property :interactive_enabled, [true, false], default: false
property :frequency_modifier, [Integer, String], default: 1
property :frequency, equal_to: [:minute,
:hourly,
:daily,
:weekly,
:monthly,
:once,
:on_logon,
:onstart,
:on_idle], default: :hourly
property :start_day, String
property :start_time, String
property :day, [String, Integer]
property :months, String
property :idle_time, Integer
property :exists, [true, false], desired_state: true
property :status, Symbol, desired_state: true
property :enabled, [true, false], desired_state: true
def load_task_hash(task_name)
Chef::Log.debug 'Looking for existing tasks'
# we use powershell_out here instead of powershell_out! because a failure implies that the task does not exist
task_script = <<-EOH
[Console]::OutputEncoding = [Text.UTF8Encoding]::UTF8
schtasks /Query /FO LIST /V /TN \"#{task_name}\"
EOH
output = powershell_out(task_script).stdout.force_encoding('UTF-8')
if output.empty?
task = false
else
task = {}
output.split("\n").map! { |line| line.split(':', 2).map!(&:strip) }.each do |field|
if field.is_a?(Array) && field[0].respond_to?(:to_sym)
task[field[0].gsub(/\s+/, '').to_sym] = field[1]
end
end
end
task
end
load_current_value do |desired|
pathed_task_name = desired.task_name.start_with?('\\') ? desired.task_name : "\\#{desired.task_name}"
task_hash = load_task_hash pathed_task_name
task_name pathed_task_name
if task_hash.respond_to?(:[]) && task_hash[:TaskName] == pathed_task_name
exists true
status :running if task_hash[:Status] == 'Running'
enabled task_hash[:ScheduledTaskState] == 'Enabled' ? true : false
cwd task_hash[:StartIn] unless task_hash[:StartIn] == 'N/A'
command task_hash[:TaskToRun]
user task_hash[:RunAsUser]
else
exists false
end
end
action :create do
if current_resource.exists && !(task_need_update? || new_resource.force)
Chef::Log.info "#{new_resource} task already exists - nothing to do"
else
converge_by("creating a new scheduled task #{new_resource.task_name}") do
validate_user_and_password
validate_interactive_setting
validate_create_frequency_modifier
validate_create_day
validate_create_months
validate_idle_time
options = {}
options['F'] = '' if new_resource.force || task_need_update?
options['SC'] = schedule
options['MO'] = new_resource.frequency_modifier if frequency_modifier_allowed
options['I'] = new_resource.idle_time unless new_resource.idle_time.nil?
options['SD'] = new_resource.start_day unless new_resource.start_day.nil?
options['ST'] = new_resource.start_time unless new_resource.start_time.nil?
options['TR'] = new_resource.command
options['RU'] = new_resource.user
options['RP'] = new_resource.password if use_password?
options['RL'] = 'HIGHEST' if new_resource.run_level == :highest
options['IT'] = '' if new_resource.interactive_enabled
options['D'] = new_resource.day if new_resource.day
options['M'] = new_resource.months unless new_resource.months.nil?
run_schtasks 'CREATE', options
cwd(new_resource.cwd) if new_resource.cwd
end
end
end
action :run do
if current_resource.exists
if current_resource.status == :running
Chef::Log.info "#{new_resource} task is currently running, skipping run"
else
converge_by("running scheduled task #{new_resource.task_name}") do
run_schtasks 'RUN'
new_resource.updated_by_last_action true
end
end
else
Chef::Log.debug "#{new_resource} task doesn't exists - nothing to do"
end
end
action :change do
if current_resource.exists
converge_by("changing scheduled task #{new_resource.task_name}") do
validate_user_and_password
validate_interactive_setting
options = {}
options['TR'] = new_resource.command if new_resource.command
options['RU'] = new_resource.user if new_resource.user
options['RP'] = new_resource.password if new_resource.password
options['SD'] = new_resource.start_day unless new_resource.start_day.nil?
options['ST'] = new_resource.start_time unless new_resource.start_time.nil?
options['IT'] = '' if new_resource.interactive_enabled
run_schtasks 'CHANGE', options
cwd(new_resource.cwd) if new_resource.cwd != current_resource.cwd
end
else
Chef::Log.debug "#{new_resource} task doesn't exists - nothing to do"
end
end
action :delete do
if current_resource.exists
converge_by("deleting scheduled task #{new_resource.task_name}") do
# always need to force deletion
run_schtasks 'DELETE', 'F' => ''
end
else
Chef::Log.debug "#{new_resource} task doesn't exists - nothing to do"
end
end
action :end do
if current_resource.exists
if current_resource.status != :running
Chef::Log.debug "#{new_resource} is not running - nothing to do"
else
converge_by("stopping scheduled task #{new_resource.task_name}") do
run_schtasks 'END'
end
end
else
Chef::Log.fatal "#{new_resource} task doesn't exist - nothing to do"
raise Errno::ENOENT, "#{new_resource}: task does not exist, cannot end"
end
end
action :enable do
if current_resource.exists
if current_resource.enabled
Chef::Log.debug "#{new_resource} already enabled - nothing to do"
else
converge_by("enabling scheduled task #{new_resource.task_name}") do
run_schtasks 'CHANGE', 'ENABLE' => ''
end
end
else
Chef::Log.fatal "#{new_resource} task doesn't exist - nothing to do"
raise Errno::ENOENT, "#{new_resource}: task does not exist, cannot enable"
end
end
action :disable do
if current_resource.exists
if current_resource.enabled
converge_by("disabling scheduled task #{new_resource.task_name}") do
run_schtasks 'CHANGE', 'DISABLE' => ''
end
else
Chef::Log.debug "#{new_resource} already disabled - nothing to do"
end
else
Chef::Log.debug "#{new_resource} task doesn't exist - nothing to do"
end
end
action_class do
# rubocop:disable Style/StringLiteralsInInterpolation
def run_schtasks(task_action, options = {})
cmd = "schtasks /#{task_action} /TN \"#{new_resource.task_name}\" "
options.keys.each do |option|
cmd += "/#{option} "
cmd += "\"#{options[option].to_s.gsub('"', "\\\"")}\" " unless options[option] == ''
end
Chef::Log.debug('running: ')
Chef::Log.debug(" #{cmd}")
shell_out!(cmd, returns: [0])
end
# rubocop:enable Style/StringLiteralsInInterpolation
def task_need_update?
# gsub needed as schtasks converts single quotes to double quotes on creation
current_resource.command != new_resource.command.tr("'", '"') ||
current_resource.user != new_resource.user
end
def cwd(folder)
Chef::Log.debug 'looking for existing tasks'
# we use shell_out here instead of shell_out! because a failure implies that the task does not exist
xml_cmd = shell_out("schtasks /Query /TN \"#{new_resource.task_name}\" /XML")
return if xml_cmd.exitstatus != 0
doc = REXML::Document.new(xml_cmd.stdout)
Chef::Log.debug 'Removing former CWD if any'
doc.root.elements.delete('Actions/Exec/WorkingDirectory')
unless folder.nil?
Chef::Log.debug 'Setting CWD as #folder'
cwd_element = REXML::Element.new('WorkingDirectory')
cwd_element.add_text(folder)
exec_element = doc.root.elements['Actions/Exec']
exec_element.add_element(cwd_element)
end
temp_task_file = ::File.join(ENV['TEMP'], 'windows_task.xml')
begin
::File.open(temp_task_file, 'w:UTF-16LE') do |f|
doc.write(f)
end
options = {}
options['RU'] = new_resource.user if new_resource.user
options['RP'] = new_resource.password if new_resource.password
options['IT'] = '' if new_resource.interactive_enabled
options['XML'] = temp_task_file
run_schtasks('DELETE', 'F' => '')
run_schtasks('CREATE', options)
ensure
::File.delete(temp_task_file)
end
end
SYSTEM_USERS = ['NT AUTHORITY\SYSTEM', 'SYSTEM', 'NT AUTHORITY\LOCALSERVICE', 'NT AUTHORITY\NETWORKSERVICE'].freeze
def validate_user_and_password
return unless new_resource.user && use_password?
return unless new_resource.password.nil?
Chef::Log.fatal "#{new_resource.task_name}: Can't specify a non-system user without a password!"
end
def validate_interactive_setting
return unless new_resource.interactive_enabled && new_resource.password.nil?
Chef::Log.fatal "#{new_resource} did not provide a password when attempting to set interactive/non-interactive."
end
def validate_create_day
return unless new_resource.day
unless [:weekly, :monthly].include?(new_resource.frequency)
raise 'day attribute is only valid for tasks that run weekly or monthly'
end
return unless new_resource.day.is_a?(String) && new_resource.day.to_i.to_s != new_resource.day
days = new_resource.day.split(',')
days.each do |day|
unless ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun', '*'].include?(day.strip.downcase)
raise 'day attribute invalid. Only valid values are: MON, TUE, WED, THU, FRI, SAT, SUN and *. Multiple values must be separated by a comma.'
end
end
end
def validate_create_months
return unless new_resource.months
unless [:monthly].include?(new_resource.frequency)
raise 'months attribute is only valid for tasks that run monthly'
end
return unless new_resource.months.is_a? String
months = new_resource.months.split(',')
months.each do |month|
unless ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC', '*'].include?(month.strip.upcase)
raise 'months attribute invalid. Only valid values are: JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC and *. Multiple values must be separated by a comma.'
end
end
end
def validate_idle_time
return unless new_resource.frequency == :on_idle
return if new_resource.idle_time.to_i > 0 && new_resource.idle_time.to_i <= 999
raise "idle_time value #{new_resource.idle_time} is invalid. Valid values for :on_idle frequency are 1 - 999."
end
def validate_create_frequency_modifier
# Currently is handled in create action 'frequency_modifier_allowed' line. Does not allow for frequency_modifier for once,onstart,onlogon,onidle
# Note that 'OnEvent' is not a supported frequency.
return if new_resource.frequency.nil? || new_resource.frequency_modifier.nil?
case new_resource.frequency
when :minute
unless new_resource.frequency_modifier.to_i > 0 && new_resource.frequency_modifier.to_i <= 1439
raise "frequency_modifier value #{new_resource.frequency_modifier} is invalid. Valid values for :minute frequency are 1 - 1439."
end
when :hourly
unless new_resource.frequency_modifier.to_i > 0 && new_resource.frequency_modifier.to_i <= 23
raise "frequency_modifier value #{new_resource.frequency_modifier} is invalid. Valid values for :hourly frequency are 1 - 23."
end
when :daily
unless new_resource.frequency_modifier.to_i > 0 && new_resource.frequency_modifier.to_i <= 365
raise "frequency_modifier value #{new_resource.frequency_modifier} is invalid. Valid values for :daily frequency are 1 - 365."
end
when :weekly
unless new_resource.frequency_modifier.to_i > 0 && new_resource.frequency_modifier.to_i <= 52
raise "frequency_modifier value #{new_resource.frequency_modifier} is invalid. Valid values for :weekly frequency are 1 - 52."
end
when :monthly
unless ('1'..'12').to_a.push('FIRST', 'SECOND', 'THIRD', 'FOURTH', 'LAST', 'LASTDAY').include?(new_resource.frequency_modifier.to_s.upcase)
raise "frequency_modifier value #{new_resource.frequency_modifier} is invalid. Valid values for :monthly frequency are 1 - 12, 'FIRST', 'SECOND', 'THIRD', 'FOURTH', 'LAST', 'LASTDAY'."
end
end
end
def use_password?
@use_password ||= !SYSTEM_USERS.include?(new_resource.user.upcase)
end
def schedule
case new_resource.frequency
when :on_logon
'ONLOGON'
when :on_idle
'ONIDLE'
else
new_resource.frequency
end
end
def frequency_modifier_allowed
case new_resource.frequency
when :minute, :hourly, :daily, :weekly
true
when :monthly
new_resource.months.nil? || %w(FIRST SECOND THIRD FOURTH LAST LASTDAY).include?(new_resource.frequency_modifier)
else
false
end
end
end

View File

@@ -1,33 +1,125 @@
#
# Author:: Doug MacEachern (<dougm@vmware.com>)
# Author:: Seth Chisamore (<schisamo@chef.io>)
# Cookbook Name:: windows
# Resource:: zipfile
#
# Copyright:: 2010, VMware, Inc.
# Copyright:: 2011-2015, 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.
#
actions :unzip, :zip
attribute :path, kind_of: String, name_attribute: true
attribute :source, kind_of: String
attribute :overwrite, kind_of: [TrueClass, FalseClass], default: false
attribute :checksum, kind_of: String
def initialize(name, run_context = nil)
super
@action = :unzip
end
#
# Author:: Doug MacEachern (<dougm@vmware.com>)
# Author:: Seth Chisamore (<schisamo@chef.io>)
# Cookbook:: windows
# Resource:: zipfile
#
# Copyright:: 2010-2017, VMware, Inc.
# Copyright:: 2011-2017, 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.
#
property :path, String, name_property: true
property :source, String
property :overwrite, [true, false], default: false
property :checksum, String
include Windows::Helper
require 'find'
action :unzip do
ensure_rubyzip_gem_installed
Chef::Log.debug("unzip #{new_resource.source} => #{new_resource.path} (overwrite=#{new_resource.overwrite})")
cache_file_path = if new_resource.source =~ %r{^(file|ftp|http|https):\/\/} # http://rubular.com/r/DGoIWjLfGI
uri = as_uri(source)
local_cache_path = "#{Chef::Config[:file_cache_path]}/#{::File.basename(::URI.unescape(uri.path))}"
Chef::Log.debug("Caching a copy of file #{new_resource.source} at #{cache_file_path}")
remote_file local_cache_path do
source new_resource.source
backup false
checksum new_resource.checksum unless new_resource.checksum.nil?
end
local_cache_path
else
new_resource.source
end
cache_file_path = win_friendly_path(cache_file_path)
converge_by("unzip #{new_resource.source}") do
ruby_block 'Unzipping' do
block do
Zip::File.open(cache_file_path) do |zip|
zip.each do |entry|
path = ::File.join(new_resource.path, entry.name)
FileUtils.mkdir_p(::File.dirname(path))
if new_resource.overwrite && ::File.exist?(path) && !::File.directory?(path)
FileUtils.rm(path)
end
zip.extract(entry, path) unless ::File.exist?(path)
end
end
end
action :run
end
end
end
action :zip do
ensure_rubyzip_gem_installed
# sanitize paths for windows.
new_resource.source.downcase.gsub!(::File::SEPARATOR, ::File::ALT_SEPARATOR)
new_resource.path.downcase.gsub!(::File::SEPARATOR, ::File::ALT_SEPARATOR)
Chef::Log.debug("zip #{new_resource.source} => #{new_resource.path} (overwrite=#{new_resource.overwrite})")
if new_resource.overwrite == false && ::File.exist?(new_resource.path)
Chef::Log.info("file #{new_resource.path} already exists and overwrite is set to false, exiting")
else
# delete the archive if it already exists, because we are recreating it.
if ::File.exist?(new_resource.path)
converge_by("delete existing file at #{new_resource.path}") do
::File.unlink(new_resource.path)
end
end
# only supporting compression of a single directory (recursively).
if ::File.directory?(new_resource.source)
converge_by("zipping #{new_resource.source} to #{new_resource.path}") do
z = Zip::File.new(new_resource.path, true)
unless new_resource.source =~ /::File::ALT_SEPARATOR$/
new_resource.source << ::File::ALT_SEPARATOR
end
Find.find(new_resource.source) do |f|
f.downcase.gsub!(::File::SEPARATOR, ::File::ALT_SEPARATOR)
# don't add root directory to the zipfile.
next if f == new_resource.source
# strip the root directory from the filename before adding it to the zipfile.
zip_fname = f.sub(new_resource.source, '')
Chef::Log.debug("adding #{zip_fname} to archive, sourcefile is: #{f}")
z.add(zip_fname, f)
end
z.close
end
else
Chef::Log.info("Single directory must be specified for compression, and #{new_resource.source} does not meet that criteria.")
end
end
end
action_class do
def ensure_rubyzip_gem_installed
require 'zip'
rescue LoadError
Chef::Log.info("Missing gem 'rubyzip'...installing now.")
chef_gem 'rubyzip' do
version node['windows']['rubyzipversion']
action :install
compile_time true
end
require 'zip'
end
end