Downgrade mysql cookbook for now
It doesn't play well with our current dev server setup
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
108
cookbooks/windows/resources/feature_dism.rb
Normal file
108
cookbooks/windows/resources/feature_dism.rb
Normal 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
|
||||
70
cookbooks/windows/resources/feature_powershell.rb
Normal file
70
cookbooks/windows/resources/feature_powershell.rb
Normal 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
|
||||
76
cookbooks/windows/resources/feature_servermanagercmd.rb
Normal file
76
cookbooks/windows/resources/feature_servermanagercmd.rb
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
289
cookbooks/windows/resources/share.rb
Normal file
289
cookbooks/windows/resources/share.rb
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user