vagrant and chef install everything
This commit is contained in:
301
ops/cookbooks/vendor/windows/resources/certificate.rb
vendored
Normal file
301
ops/cookbooks/vendor/windows/resources/certificate.rb
vendored
Normal file
@ -0,0 +1,301 @@
|
||||
#
|
||||
# Author:: Richard Lavey (richard.lavey@calastone.com)
|
||||
# Cookbook:: windows
|
||||
# Resource:: certificate
|
||||
#
|
||||
# Copyright:: 2015-2017, Calastone Ltd.
|
||||
# Copyright:: 2018-2019, Chef Software, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
require 'chef/util/path_helper'
|
||||
|
||||
chef_version_for_provides '< 14.7' if respond_to?(:chef_version_for_provides)
|
||||
resource_name :windows_certificate
|
||||
|
||||
property :source, String, name_property: true
|
||||
property :pfx_password, String
|
||||
property :private_key_acl, Array
|
||||
property :store_name, String, default: 'MY', equal_to: ['TRUSTEDPUBLISHER', 'TrustedPublisher', 'CLIENTAUTHISSUER', 'REMOTE DESKTOP', 'ROOT', 'TRUSTEDDEVICES', 'WEBHOSTING', 'CA', 'AUTHROOT', 'TRUSTEDPEOPLE', 'MY', 'SMARTCARDROOT', 'TRUST', 'DISALLOWED']
|
||||
property :user_store, [TrueClass, FalseClass], default: false
|
||||
property :cert_path, String
|
||||
property :sensitive, [ TrueClass, FalseClass ], default: lazy { |r| r.pfx_password ? true : false }
|
||||
|
||||
action :create do
|
||||
load_gem
|
||||
|
||||
# Extension of the certificate
|
||||
ext = ::File.extname(new_resource.source)
|
||||
cert_obj = fetch_cert_object(ext) # Fetch OpenSSL::X509::Certificate object
|
||||
thumbprint = OpenSSL::Digest::SHA1.new(cert_obj.to_der).to_s # Fetch its thumbprint
|
||||
|
||||
# Need to check if return value is Boolean:true
|
||||
# If not then the given certificate should be added in certstore
|
||||
if verify_cert(thumbprint) == true
|
||||
Chef::Log.debug('Certificate is already present')
|
||||
else
|
||||
converge_by("Adding certificate #{new_resource.source} into Store #{new_resource.store_name}") do
|
||||
if ext == '.pfx'
|
||||
add_pfx_cert
|
||||
else
|
||||
add_cert(cert_obj)
|
||||
end
|
||||
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)
|
||||
|
||||
powershell_script "setting the acls on #{new_resource.source} in #{cert_location}\\#{new_resource.store_name}" do
|
||||
guard_interpreter :powershell_script
|
||||
convert_boolean_return true
|
||||
code code_script
|
||||
only_if guard_script
|
||||
sensitive if new_resource.sensitive
|
||||
end
|
||||
end
|
||||
|
||||
action :delete do
|
||||
load_gem
|
||||
|
||||
cert_obj = fetch_cert
|
||||
if cert_obj
|
||||
converge_by("Deleting certificate #{new_resource.source} from Store #{new_resource.store_name}") do
|
||||
delete_cert
|
||||
end
|
||||
else
|
||||
Chef::Log.debug('Certificate not found')
|
||||
end
|
||||
end
|
||||
|
||||
action :fetch do
|
||||
load_gem
|
||||
|
||||
cert_obj = fetch_cert
|
||||
if cert_obj
|
||||
show_or_store_cert(cert_obj)
|
||||
else
|
||||
Chef::Log.debug('Certificate not found')
|
||||
end
|
||||
end
|
||||
|
||||
action :verify do
|
||||
load_gem
|
||||
|
||||
out = verify_cert
|
||||
if !!out == out
|
||||
out = out ? 'Certificate is valid' : 'Certificate not valid'
|
||||
end
|
||||
Chef::Log.info(out.to_s)
|
||||
end
|
||||
|
||||
action_class do
|
||||
require 'openssl'
|
||||
|
||||
# load the gem and rescue a gem install if it fails to load
|
||||
def load_gem
|
||||
gem 'win32-certstore', '>= 0.2.4'
|
||||
require 'win32-certstore' # until this is in core chef
|
||||
rescue LoadError
|
||||
Chef::Log.debug('Did not find win32-certstore >= 0.2.4 gem installed. Installing now')
|
||||
chef_gem 'win32-certstore' do
|
||||
compile_time true
|
||||
action :upgrade
|
||||
end
|
||||
|
||||
require 'win32-certstore'
|
||||
end
|
||||
|
||||
def add_cert(cert_obj)
|
||||
store = ::Win32::Certstore.open(new_resource.store_name)
|
||||
store.add(cert_obj)
|
||||
end
|
||||
|
||||
def add_pfx_cert
|
||||
store = ::Win32::Certstore.open(new_resource.store_name)
|
||||
store.add_pfx(new_resource.source, new_resource.pfx_password)
|
||||
end
|
||||
|
||||
def delete_cert
|
||||
store = ::Win32::Certstore.open(new_resource.store_name)
|
||||
store.delete(new_resource.source)
|
||||
end
|
||||
|
||||
def fetch_cert
|
||||
store = ::Win32::Certstore.open(new_resource.store_name)
|
||||
store.get(new_resource.source)
|
||||
end
|
||||
|
||||
# Checks whether a certificate with the given thumbprint
|
||||
# is already present and valid in certificate store
|
||||
# If the certificate is not present, verify_cert returns a String: "Certificate not found"
|
||||
# But if it is present but expired, it returns a Boolean: false
|
||||
# Otherwise, it returns a Boolean: true
|
||||
def verify_cert(thumbprint = new_resource.source)
|
||||
store = ::Win32::Certstore.open(new_resource.store_name)
|
||||
store.valid?(thumbprint)
|
||||
end
|
||||
|
||||
def show_or_store_cert(cert_obj)
|
||||
if new_resource.cert_path
|
||||
export_cert(cert_obj, new_resource.cert_path)
|
||||
if ::File.size(new_resource.cert_path) > 0
|
||||
Chef::Log.info("Certificate export in #{new_resource.cert_path}")
|
||||
else
|
||||
::File.delete(new_resource.cert_path)
|
||||
end
|
||||
else
|
||||
Chef::Log.info(cert_obj.display)
|
||||
end
|
||||
end
|
||||
|
||||
def export_cert(cert_obj, cert_path)
|
||||
out_file = ::File.new(cert_path, 'w+')
|
||||
case ::File.extname(cert_path)
|
||||
when '.pem'
|
||||
out_file.puts(cert_obj.to_pem)
|
||||
when '.der'
|
||||
out_file.puts(cert_obj.to_der)
|
||||
when '.cer'
|
||||
cert_out = powershell_out("openssl x509 -text -inform DER -in #{cert_obj.to_pem} -outform CER").stdout
|
||||
out_file.puts(cert_out)
|
||||
when '.crt'
|
||||
cert_out = powershell_out("openssl x509 -text -inform DER -in #{cert_obj.to_pem} -outform CRT").stdout
|
||||
out_file.puts(cert_out)
|
||||
when '.pfx'
|
||||
cert_out = powershell_out("openssl pkcs12 -export -nokeys -in #{cert_obj.to_pem} -outform PFX").stdout
|
||||
out_file.puts(cert_out)
|
||||
when '.p7b'
|
||||
cert_out = powershell_out("openssl pkcs7 -export -nokeys -in #{cert_obj.to_pem} -outform P7B").stdout
|
||||
out_file.puts(cert_out)
|
||||
else
|
||||
Chef::Log.info('Supported certificate format .pem, .der, .cer, .crt, .pfx and .p7b')
|
||||
end
|
||||
out_file.close
|
||||
end
|
||||
|
||||
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 = Chef::Util::PathHelper.cleanpath(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
|
||||
|
||||
# Method returns an OpenSSL::X509::Certificate object
|
||||
#
|
||||
# Based on its extension, the certificate contents are used to initialize
|
||||
# PKCS12 (PFX), PKCS7 (P7B) objects which contains OpenSSL::X509::Certificate.
|
||||
#
|
||||
# @note Other then PEM, all the certificates are usually in binary format, and hence
|
||||
# their contents are loaded by using File.binread
|
||||
#
|
||||
# @param ext [String] Extension of the certificate
|
||||
#
|
||||
# @return [OpenSSL::X509::Certificate] Object containing certificate's attributes
|
||||
#
|
||||
# @raise [OpenSSL::PKCS12::PKCS12Error] When incorrect password is provided for PFX certificate
|
||||
#
|
||||
def fetch_cert_object(ext)
|
||||
contents = if binary_cert?
|
||||
::File.binread(new_resource.source)
|
||||
else
|
||||
::File.read(new_resource.source)
|
||||
end
|
||||
|
||||
case ext
|
||||
when '.pfx'
|
||||
OpenSSL::PKCS12.new(contents, new_resource.pfx_password).certificate
|
||||
when '.p7b'
|
||||
OpenSSL::PKCS7.new(contents).certificates.first
|
||||
else
|
||||
OpenSSL::X509::Certificate.new(contents)
|
||||
end
|
||||
end
|
||||
|
||||
# @return [Boolean] Whether the certificate file is binary encoded or not
|
||||
#
|
||||
def binary_cert?
|
||||
powershell_out!("file -b --mime-encoding #{new_resource.source}").stdout.strip == 'binary'
|
||||
end
|
||||
end
|
135
ops/cookbooks/vendor/windows/resources/certificate_binding.rb
vendored
Normal file
135
ops/cookbooks/vendor/windows/resources/certificate_binding.rb
vendored
Normal file
@ -0,0 +1,135 @@
|
||||
#
|
||||
# Author:: Richard Lavey (richard.lavey@calastone.com)
|
||||
# Cookbook:: windows
|
||||
# Resource:: certificate_binding
|
||||
#
|
||||
# Copyright:: 2015-2017, Calastone Ltd.
|
||||
# Copyright:: 2018, Chef Software, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
include Chef::Mixin::PowershellOut
|
||||
include Windows::Helper
|
||||
|
||||
property :cert_name, String, name_property: 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', equal_to: ['TRUSTEDPUBLISHER', 'CLIENTAUTHISSUER', 'REMOTE DESKTOP', 'ROOT', 'TRUSTEDDEVICES', 'WEBHOSTING', 'CA', 'AUTHROOT', 'TRUSTEDPEOPLE', 'MY', 'SMARTCARDROOT', 'TRUST']
|
||||
property :exists, [true, false], desired_state: true
|
||||
|
||||
load_current_value do |desired|
|
||||
mode = desired.address.match(/(\d+\.){3}\d+|\[.+\]/).nil? ? 'hostnameport' : 'ipport'
|
||||
cmd = shell_out("#{locate_sysnative_cmd('netsh.exe')} http show sslcert #{mode}=#{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"
|
||||
mode = address_mode(current_resource.address)
|
||||
cmd << " #{mode}=#{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
|
||||
mode = address_mode(current_resource.address)
|
||||
shell_out!("#{netsh_command} http delete sslcert #{mode}=#{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
|
||||
|
||||
def address_mode(address)
|
||||
address.match(/(\d+\.){3}\d+|\[.+\]/).nil? ? 'hostnameport' : 'ipport'
|
||||
end
|
||||
end
|
30
ops/cookbooks/vendor/windows/resources/dns.rb
vendored
Normal file
30
ops/cookbooks/vendor/windows/resources/dns.rb
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
#
|
||||
# Author:: Richard Lavey (richard.lavey@calastone.com)
|
||||
# Cookbook Name:: windows
|
||||
# Resource:: dns
|
||||
#
|
||||
# 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 :host_name, kind_of: String, name_property: true, required: true
|
||||
attribute :record_type, kind_of: String, default: 'A', regex: /^(?:A|CNAME)$/
|
||||
attribute :dns_server, kind_of: String, default: '.'
|
||||
attribute :target, kind_of: [Array, String], required: true
|
||||
attribute :ttl, kind_of: Integer, required: false, default: 0
|
||||
|
||||
attr_accessor :exists
|
109
ops/cookbooks/vendor/windows/resources/http_acl.rb
vendored
Normal file
109
ops/cookbooks/vendor/windows/resources/http_acl.rb
vendored
Normal file
@ -0,0 +1,109 @@
|
||||
#
|
||||
# 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 Windows::Helper
|
||||
|
||||
property :url, String, name_property: 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>\S+)/)
|
||||
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
|
288
ops/cookbooks/vendor/windows/resources/share.rb
vendored
Normal file
288
ops/cookbooks/vendor/windows/resources/share.rb
vendored
Normal file
@ -0,0 +1,288 @@
|
||||
#
|
||||
# Author:: Sölvi Páll Ásgeirsson (<solvip@gmail.com>)
|
||||
# Author:: Richard Lavey (richard.lavey@calastone.com)
|
||||
# Author:: Tim Smith (tsmith@chef.io)
|
||||
# Cookbook:: windows
|
||||
# Resource:: share
|
||||
#
|
||||
# Copyright:: 2014-2017, Sölvi Páll Ásgeirsson.
|
||||
# Copyright:: 2018, Chef Software, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
chef_version_for_provides '< 14.7' if respond_to?(:chef_version_for_provides)
|
||||
resource_name :windows_share
|
||||
|
||||
require 'chef/json_compat'
|
||||
require 'chef/util/path_helper'
|
||||
|
||||
# Specifies a name for the SMB share. The name may be composed of any valid file name characters, but must be less than 80 characters long. The names pipe and mailslot are reserved for use by the computer.
|
||||
property :share_name, String, name_property: true
|
||||
|
||||
# Specifies the path of the location of the folder to share. The path must be fully qualified. Relative paths or paths that contain wildcard characters are not permitted.
|
||||
property :path, String
|
||||
|
||||
# Specifies an optional description of the SMB share. A description of the share is displayed by running the Get-SmbShare cmdlet. The description may not contain more than 256 characters.
|
||||
property :description, String, default: ''
|
||||
|
||||
# Specifies which accounts are granted full permission to access the share. Use a comma-separated list to specify multiple accounts. An account may not be specified more than once in the FullAccess, ChangeAccess, or ReadAccess parameter lists, but may be specified once in the FullAccess, ChangeAccess, or ReadAccess parameter list and once in the NoAccess parameter list.
|
||||
property :full_users, Array, default: [], coerce: proc { |u| u.sort }
|
||||
|
||||
# Specifies which users are granted modify permission to access the share
|
||||
property :change_users, Array, default: [], coerce: proc { |u| u.sort }
|
||||
|
||||
# Specifies which users are granted read permission to access the share. Multiple users can be specified by supplying a comma-separated list.
|
||||
property :read_users, Array, default: [], coerce: proc { |u| u.sort }
|
||||
|
||||
# Specifies the lifetime of the new SMB share. A temporary share does not persist beyond the next restart of the computer. By default, new SMB shares are persistent, and non-temporary.
|
||||
property :temporary, [true, false], default: false
|
||||
|
||||
# Specifies the scope name of the share.
|
||||
property :scope_name, String, default: '*'
|
||||
|
||||
# Specifies the continuous availability time-out for the share.
|
||||
property :ca_timeout, Integer, default: 0
|
||||
|
||||
# Indicates that the share is continuously available.
|
||||
property :continuously_available, [true, false], default: false
|
||||
|
||||
# Specifies the caching mode of the offline files for the SMB share.
|
||||
# property :caching_mode, String, equal_to: %w(None Manual Documents Programs BranchCache)
|
||||
|
||||
# Specifies the maximum number of concurrently connected users that the new SMB share may accommodate. If this parameter is set to zero (0), then the number of users is unlimited.
|
||||
property :concurrent_user_limit, Integer, default: 0
|
||||
|
||||
# Indicates that the share is encrypted.
|
||||
property :encrypt_data, [true, false], default: false
|
||||
|
||||
# Specifies which files and folders in the SMB share are visible to users. AccessBased: SMB does not the display the files and folders for a share to a user unless that user has rights to access the files and folders. By default, access-based enumeration is disabled for new SMB shares. Unrestricted: SMB displays files and folders to a user even when the user does not have permission to access the items.
|
||||
# property :folder_enumeration_mode, String, equal_to: %(AccessBased Unrestricted)
|
||||
|
||||
include Chef::Mixin::PowershellOut
|
||||
|
||||
load_current_value do |desired|
|
||||
# this command selects individual objects because EncryptData & CachingMode have underlying
|
||||
# types that get converted to their Integer values by ConvertTo-Json & we need to make sure
|
||||
# those get written out as strings
|
||||
share_state_cmd = "Get-SmbShare -Name '#{desired.share_name}' | Select-Object Name,Path, Description, Temporary, CATimeout, ContinuouslyAvailable, ConcurrentUserLimit, EncryptData | ConvertTo-Json"
|
||||
|
||||
Chef::Log.debug("Running '#{share_state_cmd}' to determine share state'")
|
||||
ps_results = powershell_out(share_state_cmd)
|
||||
|
||||
# detect a failure without raising and then set current_resource to nil
|
||||
if ps_results.error?
|
||||
Chef::Log.debug("Error fetching share state: #{ps_results.stderr}")
|
||||
current_value_does_not_exist!
|
||||
end
|
||||
|
||||
Chef::Log.debug("The Get-SmbShare results were #{ps_results.stdout}")
|
||||
results = Chef::JSONCompat.from_json(ps_results.stdout)
|
||||
|
||||
path results['Path']
|
||||
description results['Description']
|
||||
temporary results['Temporary']
|
||||
ca_timeout results['CATimeout']
|
||||
continuously_available results['ContinuouslyAvailable']
|
||||
# caching_mode results['CachingMode']
|
||||
concurrent_user_limit results['ConcurrentUserLimit']
|
||||
encrypt_data results['EncryptData']
|
||||
# folder_enumeration_mode results['FolderEnumerationMode']
|
||||
|
||||
perm_state_cmd = %(Get-SmbShareAccess -Name "#{desired.share_name}" | Select-Object AccountName,AccessControlType,AccessRight | ConvertTo-Json)
|
||||
|
||||
Chef::Log.debug("Running '#{perm_state_cmd}' to determine share permissions state'")
|
||||
ps_perm_results = powershell_out(perm_state_cmd)
|
||||
|
||||
# we raise here instead of warning like above because we'd only get here if the above Get-SmbShare
|
||||
# command was successful and that continuing would leave us with 1/2 known state
|
||||
raise "Could not determine #{desired.share_name} share permissions by running '#{perm_state_cmd}'" if ps_perm_results.error?
|
||||
|
||||
Chef::Log.debug("The Get-SmbShareAccess results were #{ps_perm_results.stdout}")
|
||||
|
||||
f_users, c_users, r_users = parse_permissions(ps_perm_results.stdout)
|
||||
|
||||
full_users f_users
|
||||
change_users c_users
|
||||
read_users r_users
|
||||
end
|
||||
|
||||
def after_created
|
||||
raise 'The windows_share resource relies on PowerShell cmdlets not present in Windows releases prior to 8/2012. Cannot continue!' if node['platform_version'].to_f < 6.3
|
||||
end
|
||||
|
||||
# given the string output of Get-SmbShareAccess parse out
|
||||
# arrays of full access users, change users, and read only users
|
||||
def parse_permissions(results_string)
|
||||
json_results = Chef::JSONCompat.from_json(results_string)
|
||||
json_results = [json_results] unless json_results.is_a?(Array) # single result is not an array
|
||||
|
||||
f_users = []
|
||||
c_users = []
|
||||
r_users = []
|
||||
|
||||
json_results.each do |perm|
|
||||
next unless perm['AccessControlType'] == 0 # allow
|
||||
case perm['AccessRight']
|
||||
when 0 then f_users << stripped_account(perm['AccountName']) # 0 full control
|
||||
when 1 then c_users << stripped_account(perm['AccountName']) # 1 == change
|
||||
when 2 then r_users << stripped_account(perm['AccountName']) # 2 == read
|
||||
end
|
||||
end
|
||||
[f_users, c_users, r_users]
|
||||
end
|
||||
|
||||
# local names are returned from Get-SmbShareAccess in the full format MACHINE\\NAME
|
||||
# but users of this resource would simply say NAME so we need to strip the values for comparison
|
||||
def stripped_account(name)
|
||||
name.slice!("#{node['hostname']}\\")
|
||||
name
|
||||
end
|
||||
|
||||
action :create do
|
||||
# we do this here instead of requiring the property because :delete doesn't need path set
|
||||
raise 'No path property set' unless new_resource.path
|
||||
|
||||
converge_if_changed do
|
||||
# you can't actually change the path so you have to delete the old share first
|
||||
if different_path?
|
||||
Chef::Log.debug('The path has changed so we will delete and recreate share')
|
||||
delete_share
|
||||
create_share
|
||||
elsif current_resource.nil?
|
||||
# powershell cmdlet for create is different than updates
|
||||
Chef::Log.debug('The current resource is nil so we will create a new share')
|
||||
create_share
|
||||
else
|
||||
Chef::Log.debug('The current resource was not nil so we will update an existing share')
|
||||
update_share
|
||||
end
|
||||
|
||||
# creating the share does not set permissions so we need to update
|
||||
update_permissions
|
||||
end
|
||||
end
|
||||
|
||||
action :delete do
|
||||
if current_resource.nil?
|
||||
Chef::Log.debug("#{new_resource.share_name} does not exist - nothing to do")
|
||||
else
|
||||
converge_by("delete #{new_resource.share_name}") do
|
||||
delete_share
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
action_class do
|
||||
def different_path?
|
||||
return false if current_resource.nil? # going from nil to something isn't different for our concerns
|
||||
return false if current_resource.path == Chef::Util::PathHelper.cleanpath(new_resource.path)
|
||||
true
|
||||
end
|
||||
|
||||
def delete_share
|
||||
delete_command = "Remove-SmbShare -Name '#{new_resource.share_name}' -Force"
|
||||
|
||||
Chef::Log.debug("Running '#{delete_command}' to remove the share")
|
||||
powershell_out!(delete_command)
|
||||
end
|
||||
|
||||
def update_share
|
||||
update_command = "Set-SmbShare -Name '#{new_resource.share_name}' -Description '#{new_resource.description}' -Force"
|
||||
|
||||
Chef::Log.debug("Running '#{update_command}' to update the share")
|
||||
powershell_out!(update_command)
|
||||
end
|
||||
|
||||
def create_share
|
||||
raise "#{new_resource.path} is missing or not a directory. Shares cannot be created if the path doesn't first exist." unless ::File.directory? new_resource.path
|
||||
|
||||
share_cmd = "New-SmbShare -Name '#{new_resource.share_name}' -Path '#{Chef::Util::PathHelper.cleanpath(new_resource.path)}' -Description '#{new_resource.description}' -ConcurrentUserLimit #{new_resource.concurrent_user_limit} -CATimeout #{new_resource.ca_timeout} -EncryptData:#{bool_string(new_resource.encrypt_data)} -ContinuouslyAvailable:#{bool_string(new_resource.continuously_available)}"
|
||||
share_cmd << " -ScopeName #{new_resource.scope_name}" unless new_resource.scope_name == '*' # passing * causes the command to fail
|
||||
share_cmd << " -Temporary:#{bool_string(new_resource.temporary)}" if new_resource.temporary # only set true
|
||||
|
||||
Chef::Log.debug("Running '#{share_cmd}' to create the share")
|
||||
powershell_out!(share_cmd)
|
||||
|
||||
# New-SmbShare adds the "Everyone" user with read access no matter what so we need to remove it
|
||||
# before we add our permissions
|
||||
revoke_user_permissions(['Everyone'])
|
||||
end
|
||||
|
||||
# determine what users in the current state don't exist in the desired state
|
||||
# users/groups will have their permissions updated with the same command that
|
||||
# sets it, but removes must be performed with Revoke-SmbShareAccess
|
||||
def users_to_revoke
|
||||
@users_to_revoke ||= begin
|
||||
# if the resource doesn't exist then nothing needs to be revoked
|
||||
if current_resource.nil?
|
||||
[]
|
||||
else # if it exists then calculate the current to new resource diffs
|
||||
(current_resource.full_users + current_resource.change_users + current_resource.read_users) - (new_resource.full_users + new_resource.change_users + new_resource.read_users)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# update existing permissions on a share
|
||||
def update_permissions
|
||||
# revoke any users that had something, but now has nothing
|
||||
revoke_user_permissions(users_to_revoke) unless users_to_revoke.empty?
|
||||
|
||||
# set permissions for each of the permission types
|
||||
%w(full read change).each do |perm_type|
|
||||
# set permissions for a brand new share OR
|
||||
# update permissions if the current state and desired state differ
|
||||
next unless permissions_need_update?(perm_type)
|
||||
grant_command = "Grant-SmbShareAccess -Name '#{new_resource.share_name}' -AccountName \"#{new_resource.send("#{perm_type}_users").join('","')}\" -Force -AccessRight #{perm_type}"
|
||||
|
||||
Chef::Log.debug("Running '#{grant_command}' to update the share permissions")
|
||||
powershell_out!(grant_command)
|
||||
end
|
||||
end
|
||||
|
||||
# determine if permissions need to be updated.
|
||||
# Brand new share with no permissions defined: no
|
||||
# Brand new share with permissions defined: yes
|
||||
# Existing share with differing permissions: yes
|
||||
#
|
||||
# @param [String] type the permissions type (Full, Read, or Change)
|
||||
def permissions_need_update?(type)
|
||||
property_name = "#{type}_users"
|
||||
|
||||
# brand new share, but nothing to set
|
||||
return false if current_resource.nil? && new_resource.send(property_name).empty?
|
||||
|
||||
# brand new share with new permissions to set
|
||||
return true if current_resource.nil? && !new_resource.send(property_name).empty?
|
||||
|
||||
# there's a difference between the current and desired state
|
||||
return true unless (new_resource.send(property_name) - current_resource.send(property_name)).empty?
|
||||
|
||||
# anything else
|
||||
false
|
||||
end
|
||||
|
||||
# revoke user permissions from a share
|
||||
# @param [Array] users
|
||||
def revoke_user_permissions(users)
|
||||
revoke_command = "Revoke-SmbShareAccess -Name '#{new_resource.share_name}' -AccountName \"#{users.join('","')}\" -Force"
|
||||
Chef::Log.debug("Running '#{revoke_command}' to revoke share permissions")
|
||||
powershell_out!(revoke_command)
|
||||
end
|
||||
|
||||
# convert True/False into "$True" & "$False"
|
||||
def bool_string(bool)
|
||||
# bool ? 1 : 0
|
||||
bool ? '$true' : '$false'
|
||||
end
|
||||
end
|
40
ops/cookbooks/vendor/windows/resources/user_privilege.rb
vendored
Normal file
40
ops/cookbooks/vendor/windows/resources/user_privilege.rb
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
#
|
||||
# Author:: Jared Kauppila (<jared@kauppi.la>)
|
||||
# Cookbook:: windows
|
||||
# Resource:: user_privilege
|
||||
#
|
||||
|
||||
property :principal, String, name_property: true
|
||||
property :privilege, [Array, String], required: true, coerce: proc { |v| [*v].sort }
|
||||
|
||||
action :add do
|
||||
([*new_resource.privilege] - [*current_resource.privilege]).each do |user_right|
|
||||
converge_by("adding user privilege #{user_right}") do
|
||||
Chef::ReservedNames::Win32::Security.add_account_right(new_resource.principal, user_right)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
action :remove do
|
||||
if Gem::Version.new(Chef::VERSION) < Gem::Version.new('14.4.10')
|
||||
Chef::Log.warn('Chef 14.4.10 is required to use windows_privilege remove action')
|
||||
else
|
||||
curr_res_privilege = current_resource.privilege
|
||||
new_res_privilege = new_resource.privilege
|
||||
missing_res_privileges = (new_res_privilege - curr_res_privilege)
|
||||
|
||||
if missing_res_privileges
|
||||
Chef::Log.info("Privilege: #{missing_res_privileges.join(', ')} not present. Unable to delete")
|
||||
end
|
||||
|
||||
(new_res_privilege - missing_res_privileges).each do |user_right|
|
||||
converge_by("removing user privilege #{user_right}") do
|
||||
Chef::ReservedNames::Win32::Security.remove_account_right(new_resource.principal, user_right)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
load_current_value do |desired|
|
||||
privilege Chef::ReservedNames::Win32::Security.get_account_right(desired.principal)
|
||||
end
|
127
ops/cookbooks/vendor/windows/resources/zipfile.rb
vendored
Normal file
127
ops/cookbooks/vendor/windows/resources/zipfile.rb
vendored
Normal file
@ -0,0 +1,127 @@
|
||||
#
|
||||
# Author:: Doug MacEachern (<dougm@vmware.com>)
|
||||
# Author:: Seth Chisamore (<schisamo@chef.io>)
|
||||
# Author:: Wade Peacock (<wade.peacock@visioncritical.com>)
|
||||
# Cookbook:: windows
|
||||
# Resource:: zipfile
|
||||
#
|
||||
# Copyright:: 2010-2017, VMware, Inc.
|
||||
# Copyright:: 2011-2018, Chef Software, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
require 'chef/util/path_helper'
|
||||
|
||||
property :path, String, name_property: true
|
||||
property :source, String
|
||||
property :overwrite, [true, false], default: false
|
||||
property :checksum, String
|
||||
|
||||
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(new_resource.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 = Chef::Util::PathHelper.cleanpath(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
|
||||
include Windows::Helper
|
||||
require 'find'
|
||||
|
||||
def ensure_rubyzip_gem_installed
|
||||
require 'zip'
|
||||
rescue LoadError
|
||||
Chef::Log.info("Missing gem 'rubyzip'...installing now.")
|
||||
chef_gem 'rubyzip' do
|
||||
action :install
|
||||
compile_time true
|
||||
end
|
||||
require 'zip'
|
||||
end
|
||||
end
|
Reference in New Issue
Block a user