chef 14.6.47 → 14.7.17
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/chef-universal-mingw32.gemspec +1 -0
- data/lib/chef/application/apply.rb +6 -4
- data/lib/chef/knife/config_list_profiles.rb +1 -1
- data/lib/chef/knife/list.rb +1 -1
- data/lib/chef/knife/raw.rb +1 -1
- data/lib/chef/knife/role_env_run_list_clear.rb +1 -1
- data/lib/chef/knife/role_env_run_list_remove.rb +1 -1
- data/lib/chef/knife/role_env_run_list_replace.rb +1 -1
- data/lib/chef/knife/role_env_run_list_set.rb +1 -1
- data/lib/chef/knife/role_run_list_clear.rb +1 -1
- data/lib/chef/knife/role_run_list_remove.rb +1 -1
- data/lib/chef/knife/role_run_list_replace.rb +1 -1
- data/lib/chef/knife/role_run_list_set.rb +1 -1
- data/lib/chef/knife/show.rb +1 -1
- data/lib/chef/knife/upload.rb +1 -1
- data/lib/chef/knife/xargs.rb +1 -1
- data/lib/chef/node/attribute.rb +189 -185
- data/lib/chef/platform/rebooter.rb +1 -0
- data/lib/chef/property.rb +25 -1
- data/lib/chef/provider/windows_task.rb +19 -6
- data/lib/chef/resource.rb +17 -2
- data/lib/chef/resource/apt_preference.rb +2 -1
- data/lib/chef/resource/apt_repository.rb +1 -1
- data/lib/chef/resource/apt_update.rb +1 -0
- data/lib/chef/resource/build_essential.rb +1 -1
- data/lib/chef/resource/cab_package.rb +1 -1
- data/lib/chef/resource/chef_gem.rb +1 -1
- data/lib/chef/resource/cookbook_file.rb +2 -1
- data/lib/chef/resource/cron_d.rb +1 -1
- data/lib/chef/resource/dmg_package.rb +52 -40
- data/lib/chef/resource/execute.rb +2 -2
- data/lib/chef/resource/file.rb +1 -1
- data/lib/chef/resource/group.rb +2 -2
- data/lib/chef/resource/homebrew_cask.rb +3 -2
- data/lib/chef/resource/homebrew_tap.rb +2 -2
- data/lib/chef/resource/hostname.rb +2 -2
- data/lib/chef/resource/kernel_module.rb +19 -13
- data/lib/chef/resource/locale.rb +1 -0
- data/lib/chef/resource/macos_userdefaults.rb +7 -6
- data/lib/chef/resource/mdadm.rb +3 -2
- data/lib/chef/resource/mount.rb +3 -3
- data/lib/chef/resource/openssl_dhparam.rb +1 -1
- data/lib/chef/resource/openssl_ec_private_key.rb +1 -1
- data/lib/chef/resource/openssl_ec_public_key.rb +3 -3
- data/lib/chef/resource/openssl_rsa_private_key.rb +2 -2
- data/lib/chef/resource/openssl_rsa_public_key.rb +3 -3
- data/lib/chef/resource/openssl_x509_certificate.rb +1 -1
- data/lib/chef/resource/openssl_x509_crl.rb +1 -1
- data/lib/chef/resource/openssl_x509_request.rb +1 -1
- data/lib/chef/resource/package.rb +1 -1
- data/lib/chef/resource/powershell_package.rb +1 -1
- data/lib/chef/resource/remote_directory.rb +2 -2
- data/lib/chef/resource/rhsm_errata.rb +1 -1
- data/lib/chef/resource/rhsm_errata_level.rb +1 -1
- data/lib/chef/resource/rhsm_register.rb +7 -7
- data/lib/chef/resource/rhsm_repo.rb +1 -1
- data/lib/chef/resource/rhsm_subscription.rb +1 -1
- data/lib/chef/resource/sudo.rb +8 -8
- data/lib/chef/resource/swap_file.rb +1 -1
- data/lib/chef/resource/sysctl.rb +2 -2
- data/lib/chef/resource/systemd_unit.rb +19 -11
- data/lib/chef/resource/timezone.rb +60 -32
- data/lib/chef/resource/windows_ad_join.rb +1 -1
- data/lib/chef/resource/windows_auto_run.rb +1 -1
- data/lib/chef/resource/windows_certificate.rb +269 -0
- data/lib/chef/resource/windows_env.rb +12 -3
- data/lib/chef/resource/windows_feature.rb +6 -11
- data/lib/chef/resource/windows_feature_dism.rb +2 -2
- data/lib/chef/resource/windows_feature_powershell.rb +5 -9
- data/lib/chef/resource/windows_firewall_rule.rb +205 -0
- data/lib/chef/resource/windows_path.rb +3 -1
- data/lib/chef/resource/windows_printer.rb +2 -2
- data/lib/chef/resource/windows_printer_port.rb +3 -3
- data/lib/chef/resource/windows_service.rb +1 -0
- data/lib/chef/resource/windows_share.rb +315 -0
- data/lib/chef/resource/windows_task.rb +4 -0
- data/lib/chef/resource/windows_workgroup.rb +6 -4
- data/lib/chef/resource/yum_repository.rb +13 -7
- data/lib/chef/resource/zypper_package.rb +2 -2
- data/lib/chef/resource/zypper_repository.rb +2 -1
- data/lib/chef/resource_inspector.rb +4 -4
- data/lib/chef/resources.rb +3 -0
- data/lib/chef/version.rb +1 -1
- data/spec/functional/resource/windows_task_spec.rb +39 -0
- data/spec/support/platform_helpers.rb +2 -1
- data/spec/unit/resource/windows_certificate.rb +46 -0
- data/spec/unit/resource/windows_firewall_rule_spec.rb +401 -0
- data/spec/unit/resource/windows_share.rb +39 -0
- metadata +10 -4
@@ -31,9 +31,18 @@ class Chef
|
|
31
31
|
default_action :create
|
32
32
|
allowed_actions :create, :delete, :modify
|
33
33
|
|
34
|
-
property :key_name, String,
|
35
|
-
|
36
|
-
|
34
|
+
property :key_name, String,
|
35
|
+
description: "The name of the key that is to be created, deleted, or modified.",
|
36
|
+
identity: true, name_property: true
|
37
|
+
|
38
|
+
property :value, String,
|
39
|
+
description: "The value of the environmental variable to set.",
|
40
|
+
required: true
|
41
|
+
|
42
|
+
property :delim, [ String, nil, false ],
|
43
|
+
description: "The delimiter that is used to separate multiple values for a single key.",
|
44
|
+
desired_state: false
|
45
|
+
|
37
46
|
property :user, String, default: "<System>"
|
38
47
|
end
|
39
48
|
end
|
@@ -24,35 +24,30 @@ class Chef
|
|
24
24
|
resource_name :windows_feature
|
25
25
|
provides(:windows_feature) { true }
|
26
26
|
|
27
|
-
description "Use the windows_feature resource to add, remove or delete Windows features and roles. This resource calls"
|
28
|
-
" the 'windows_feature_dism' or 'windows_feature_powershell' resources depending on the specified installation"\
|
29
|
-
" method and defaults to dism, which is available on both Workstation and Server editions of Windows."
|
27
|
+
description "Use the windows_feature resource to add, remove or entirely delete Windows features and roles. This resource calls the 'windows_feature_dism' or 'windows_feature_powershell' resources depending on the specified installation method and defaults to dism, which is available on both Workstation and Server editions of Windows."
|
30
28
|
introduced "14.0"
|
31
29
|
|
32
30
|
property :feature_name, [Array, String],
|
33
|
-
description: "The name of the feature
|
34
|
-
" names depending on the underlying resource being used (ie DHCPServer vs DHCP;"\
|
35
|
-
" DNS-Server-Full-Role vs DNS).",
|
31
|
+
description: "The name of the feature(s) or role(s) to install, if it differs from the resource block name. The same feature may have different names depending on the underlying installation method being used (ie DHCPServer vs DHCP; DNS-Server-Full-Role vs DNS).",
|
36
32
|
name_property: true
|
37
33
|
|
38
34
|
property :source, String,
|
39
|
-
description: "
|
35
|
+
description: "Specify a local repository for the feature install."
|
40
36
|
|
41
37
|
property :all, [TrueClass, FalseClass],
|
42
38
|
description: "Install all sub features.",
|
43
39
|
default: false
|
44
40
|
|
45
41
|
property :management_tools, [TrueClass, FalseClass],
|
46
|
-
description: "Install all applicable management tools
|
42
|
+
description: "Install all applicable management tools for the roles, role services, or features (PowerShell-only).",
|
47
43
|
default: false
|
48
44
|
|
49
45
|
property :install_method, Symbol,
|
50
|
-
description: "
|
51
|
-
" between the two installation methods.",
|
46
|
+
description: "The underlying installation method to use for feature installation. Specify ':windows_feature_dism' for DISM or ':windows_feature_powershell' for PowerShell.",
|
52
47
|
equal_to: [:windows_feature_dism, :windows_feature_powershell, :windows_feature_servermanagercmd]
|
53
48
|
|
54
49
|
property :timeout, Integer,
|
55
|
-
description: "Specifies a timeout (in seconds) for feature
|
50
|
+
description: "Specifies a timeout (in seconds) for the feature installation.",
|
56
51
|
default: 600
|
57
52
|
|
58
53
|
action :install do
|
@@ -25,7 +25,7 @@ class Chef
|
|
25
25
|
resource_name :windows_feature_dism
|
26
26
|
provides(:windows_feature_dism) { true }
|
27
27
|
|
28
|
-
description "Use the windows_feature_dism resource to add, remove or delete Windows features and roles using DISM"
|
28
|
+
description "Use the windows_feature_dism resource to add, remove, or entirely delete Windows features and roles using DISM."
|
29
29
|
introduced "14.0"
|
30
30
|
|
31
31
|
property :feature_name, [Array, String],
|
@@ -41,7 +41,7 @@ class Chef
|
|
41
41
|
default: false
|
42
42
|
|
43
43
|
property :timeout, Integer,
|
44
|
-
description: "Specifies a timeout (in seconds) for feature
|
44
|
+
description: "Specifies a timeout (in seconds) for the feature installation.",
|
45
45
|
default: 600
|
46
46
|
|
47
47
|
# @return [Array] lowercase the array unless we're on < Windows 2012
|
@@ -27,27 +27,23 @@ class Chef
|
|
27
27
|
resource_name :windows_feature_powershell
|
28
28
|
provides(:windows_feature_powershell) { true }
|
29
29
|
|
30
|
-
description "Use the windows_feature_powershell resource to add, remove or"
|
31
|
-
" delete Windows features and roles using PowerShell. This resource"\
|
32
|
-
" offers significant speed benefits over the windows_feature_dism resource,"\
|
33
|
-
" but requires installing the Remote Server Administration Tools on"\
|
34
|
-
" non-server releases of Windows"
|
30
|
+
description "Use the windows_feature_powershell resource to add, remove, or entirely delete Windows features and roles using PowerShell. This resource offers significant speed benefits over the windows_feature_dism resource, but requires installing the Remote Server Administration Tools on non-server releases of Windows."
|
35
31
|
introduced "14.0"
|
36
32
|
|
37
33
|
property :feature_name, [Array, String],
|
38
|
-
description: "The name of the feature
|
34
|
+
description: "The name of the feature(s) or role(s) to install, if it differs from the resource block name.",
|
39
35
|
coerce: proc { |x| to_formatted_array(x) },
|
40
36
|
name_property: true
|
41
37
|
|
42
38
|
property :source, String,
|
43
|
-
description: "
|
39
|
+
description: "Specify a local repository for the feature install."
|
44
40
|
|
45
41
|
property :all, [TrueClass, FalseClass],
|
46
|
-
description: "Install all
|
42
|
+
description: "Install all subfeatures. When set to 'true', this is the equivalent of specifying the '-InstallAllSubFeatures' switch with 'Add-WindowsFeature'.",
|
47
43
|
default: false
|
48
44
|
|
49
45
|
property :timeout, Integer,
|
50
|
-
description: "Specifies a timeout (in seconds) for feature
|
46
|
+
description: "Specifies a timeout (in seconds) for the feature installation.",
|
51
47
|
default: 600
|
52
48
|
|
53
49
|
property :management_tools, [TrueClass, FalseClass],
|
@@ -0,0 +1,205 @@
|
|
1
|
+
# Author:: Matt Clifton (spartacus003@hotmail.com)
|
2
|
+
# Author:: Matt Stratton (matt.stratton@gmail.com)
|
3
|
+
# Author:: Tor Magnus Rakvåg (tor.magnus@outlook.com)
|
4
|
+
# Author:: Tim Smith (tsmith@chef.io)
|
5
|
+
# Copyright:: 2013-2015 Matt Clifton
|
6
|
+
# Copyright:: 2018, Chef Software, Inc.
|
7
|
+
# Copyright:: 2018, Intility AS
|
8
|
+
#
|
9
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
10
|
+
# you may not use this file except in compliance with the License.
|
11
|
+
# You may obtain a copy of the License at
|
12
|
+
#
|
13
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
14
|
+
#
|
15
|
+
# Unless required by applicable law or agreed to in writing, software
|
16
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
17
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
18
|
+
# See the License for the specific language governing permissions and
|
19
|
+
# limitations under the License.
|
20
|
+
#
|
21
|
+
|
22
|
+
require "chef/json_compat"
|
23
|
+
|
24
|
+
class Chef
|
25
|
+
class Resource
|
26
|
+
class WindowsFirewallRule < Chef::Resource
|
27
|
+
preview_resource true
|
28
|
+
resource_name :windows_firewall_rule
|
29
|
+
|
30
|
+
description "Use the windows_firewall_rule resource to create, change or remove windows firewall rules."
|
31
|
+
introduced "14.7"
|
32
|
+
|
33
|
+
property :rule_name, String,
|
34
|
+
name_property: true,
|
35
|
+
description: "The name to assign to the firewall rule."
|
36
|
+
|
37
|
+
property :description, String,
|
38
|
+
default: "Firewall rule",
|
39
|
+
description: "The description to assign to the firewall rule."
|
40
|
+
|
41
|
+
property :local_address, String,
|
42
|
+
description: "The local address the firewall rule applies to."
|
43
|
+
|
44
|
+
property :local_port, [String, Integer, Array],
|
45
|
+
# split various formats of comma separated lists and provide a sorted array of strings to match PS output
|
46
|
+
coerce: proc { |d| d.is_a?(String) ? d.split(/\s*,\s*/).sort : Array(d).sort.map { |x| x.to_s } },
|
47
|
+
description: "The local port the firewall rule applies to."
|
48
|
+
|
49
|
+
property :remote_address, String,
|
50
|
+
description: "The remote address the firewall rule applies to."
|
51
|
+
|
52
|
+
property :remote_port, [String, Integer, Array],
|
53
|
+
# split various formats of comma separated lists and provide a sorted array of strings to match PS output
|
54
|
+
coerce: proc { |d| d.is_a?(String) ? d.split(/\s*,\s*/).sort : Array(d).sort.map { |x| x.to_s } },
|
55
|
+
description: "The remote port the firewall rule applies to."
|
56
|
+
|
57
|
+
property :direction, [Symbol, String],
|
58
|
+
default: :inbound, equal_to: [:inbound, :outbound],
|
59
|
+
description: "The direction of the firewall rule. Direction means either inbound or outbound traffic.",
|
60
|
+
coerce: proc { |d| d.is_a?(String) ? d.downcase.to_sym : d }
|
61
|
+
|
62
|
+
property :protocol, String,
|
63
|
+
default: "TCP",
|
64
|
+
description: "The protocol the firewall rule applies to."
|
65
|
+
|
66
|
+
property :firewall_action, [Symbol, String],
|
67
|
+
default: :allow, equal_to: [:allow, :block, :notconfigured],
|
68
|
+
description: "The action of the firewall rule.",
|
69
|
+
coerce: proc { |f| f.is_a?(String) ? f.downcase.to_sym : f }
|
70
|
+
|
71
|
+
property :profile, [Symbol, String],
|
72
|
+
default: :any, equal_to: [:public, :private, :domain, :any, :notapplicable],
|
73
|
+
description: "The profile the firewall rule applies to.",
|
74
|
+
coerce: proc { |p| p.is_a?(String) ? p.downcase.to_sym : p }
|
75
|
+
|
76
|
+
property :program, String,
|
77
|
+
description: "The program the firewall rule applies to."
|
78
|
+
|
79
|
+
property :service, String,
|
80
|
+
description: "The service the firewall rule applies to."
|
81
|
+
|
82
|
+
property :interface_type, [Symbol, String],
|
83
|
+
default: :any, equal_to: [:any, :wireless, :wired, :remoteaccess],
|
84
|
+
description: "The interface type the firewall rule applies to.",
|
85
|
+
coerce: proc { |i| i.is_a?(String) ? i.downcase.to_sym : i }
|
86
|
+
|
87
|
+
property :enabled, [TrueClass, FalseClass],
|
88
|
+
default: true,
|
89
|
+
description: "Whether or not to enable the firewall rule."
|
90
|
+
|
91
|
+
alias_method :localip, :local_address
|
92
|
+
alias_method :remoteip, :remote_address
|
93
|
+
alias_method :localport, :local_port
|
94
|
+
alias_method :remoteport, :remote_port
|
95
|
+
alias_method :interfacetype, :interface_type
|
96
|
+
|
97
|
+
load_current_value do
|
98
|
+
load_state_cmd = load_firewall_state(rule_name)
|
99
|
+
output = powershell_out(load_state_cmd)
|
100
|
+
if output.stdout.empty?
|
101
|
+
current_value_does_not_exist!
|
102
|
+
else
|
103
|
+
state = Chef::JSONCompat.from_json(output.stdout)
|
104
|
+
end
|
105
|
+
local_address state["local_address"]
|
106
|
+
local_port Array(state["local_port"]).sort
|
107
|
+
remote_address state["remote_address"]
|
108
|
+
remote_port Array(state["remote_port"]).sort
|
109
|
+
direction state["direction"]
|
110
|
+
protocol state["protocol"]
|
111
|
+
firewall_action state["firewall_action"]
|
112
|
+
profile state["profile"]
|
113
|
+
program state["program"]
|
114
|
+
service state["service"]
|
115
|
+
interface_type state["interface_type"]
|
116
|
+
enabled state["enabled"]
|
117
|
+
end
|
118
|
+
|
119
|
+
action :create do
|
120
|
+
description "Create a Windows firewall entry."
|
121
|
+
|
122
|
+
if current_resource
|
123
|
+
converge_if_changed :rule_name, :local_address, :local_port, :remote_address, :remote_port, :direction,
|
124
|
+
:protocol, :firewall_action, :profile, :program, :service, :interface_type, :enabled do
|
125
|
+
cmd = firewall_command("Set")
|
126
|
+
powershell_out!(cmd)
|
127
|
+
end
|
128
|
+
else
|
129
|
+
converge_by("create firewall rule #{new_resource.rule_name}") do
|
130
|
+
cmd = firewall_command("New")
|
131
|
+
powershell_out!(cmd)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
action :delete do
|
137
|
+
description "Delete an existing Windows firewall entry."
|
138
|
+
|
139
|
+
if current_resource
|
140
|
+
converge_by("delete firewall rule #{new_resource.rule_name}") do
|
141
|
+
powershell_out!("Remove-NetFirewallRule -Name '#{new_resource.rule_name}'")
|
142
|
+
end
|
143
|
+
else
|
144
|
+
Chef::Log.info("Firewall rule \"#{new_resource.rule_name}\" doesn't exist. Skipping.")
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
action_class do
|
149
|
+
# build the command to create a firewall rule based on new_resource values
|
150
|
+
# @return [String] firewall create command
|
151
|
+
def firewall_command(cmdlet_type)
|
152
|
+
cmd = "#{cmdlet_type}-NetFirewallRule -Name '#{new_resource.rule_name}'"
|
153
|
+
cmd << " -DisplayName '#{new_resource.rule_name}'" if cmdlet_type == "New"
|
154
|
+
cmd << " -Description '#{new_resource.description}'" if new_resource.description
|
155
|
+
cmd << " -LocalAddress '#{new_resource.local_address}'" if new_resource.local_address
|
156
|
+
cmd << " -LocalPort #{new_resource.local_port.join(',')}" if new_resource.local_port
|
157
|
+
cmd << " -RemoteAddress '#{new_resource.remote_address}'" if new_resource.remote_address
|
158
|
+
cmd << " -RemotePort #{new_resource.remote_port.join(',')}" if new_resource.remote_port
|
159
|
+
cmd << " -Direction '#{new_resource.direction}'" if new_resource.direction
|
160
|
+
cmd << " -Protocol '#{new_resource.protocol}'" if new_resource.protocol
|
161
|
+
cmd << " -Action '#{new_resource.firewall_action}'" if new_resource.firewall_action
|
162
|
+
cmd << " -Profile '#{new_resource.profile}'" if new_resource.profile
|
163
|
+
cmd << " -Program '#{new_resource.program}'" if new_resource.program
|
164
|
+
cmd << " -Service '#{new_resource.service}'" if new_resource.service
|
165
|
+
cmd << " -InterfaceType '#{new_resource.interface_type}'" if new_resource.interface_type
|
166
|
+
cmd << " -Enabled '#{new_resource.enabled}'"
|
167
|
+
|
168
|
+
cmd
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
private
|
173
|
+
|
174
|
+
# build the command to load the current resource
|
175
|
+
# # @return [String] current firewall state
|
176
|
+
def load_firewall_state(rule_name)
|
177
|
+
<<-EOH
|
178
|
+
Remove-TypeData System.Array # workaround for PS bug here: https://bit.ly/2SRMQ8M
|
179
|
+
$rule = Get-NetFirewallRule -Name '#{rule_name}'
|
180
|
+
$addressFilter = $rule | Get-NetFirewallAddressFilter
|
181
|
+
$portFilter = $rule | Get-NetFirewallPortFilter
|
182
|
+
$applicationFilter = $rule | Get-NetFirewallApplicationFilter
|
183
|
+
$serviceFilter = $rule | Get-NetFirewallServiceFilter
|
184
|
+
$interfaceTypeFilter = $rule | Get-NetFirewallInterfaceTypeFilter
|
185
|
+
([PSCustomObject]@{
|
186
|
+
rule_name = $rule.Name
|
187
|
+
description = $rule.Description
|
188
|
+
local_address = $addressFilter.LocalAddress
|
189
|
+
local_port = $portFilter.LocalPort
|
190
|
+
remote_address = $addressFilter.RemoteAddress
|
191
|
+
remote_port = $portFilter.RemotePort
|
192
|
+
direction = $rule.Direction.ToString()
|
193
|
+
protocol = $portFilter.Protocol
|
194
|
+
firewall_action = $rule.Action.ToString()
|
195
|
+
profile = $rule.Profile.ToString()
|
196
|
+
program = $applicationFilter.Program
|
197
|
+
service = $serviceFilter.Service
|
198
|
+
interface_type = $interfaceTypeFilter.InterfaceType.ToString()
|
199
|
+
enabled = [bool]::Parse($rule.Enabled.ToString())
|
200
|
+
}) | ConvertTo-Json
|
201
|
+
EOH
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
@@ -42,7 +42,7 @@ class Chef
|
|
42
42
|
default: false
|
43
43
|
|
44
44
|
property :driver_name, String,
|
45
|
-
description: "
|
45
|
+
description: "The exact name of printer driver installed on the system.",
|
46
46
|
required: true
|
47
47
|
|
48
48
|
property :location, String,
|
@@ -61,7 +61,7 @@ class Chef
|
|
61
61
|
regex: Resolv::IPv4::Regex
|
62
62
|
|
63
63
|
property :exists, [TrueClass, FalseClass],
|
64
|
-
|
64
|
+
skip_docs: true
|
65
65
|
|
66
66
|
PRINTERS_REG_KEY = 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\\'.freeze unless defined?(PRINTERS_REG_KEY)
|
67
67
|
|
@@ -34,7 +34,7 @@ class Chef
|
|
34
34
|
name_property: true,
|
35
35
|
regex: Resolv::IPv4::Regex,
|
36
36
|
validation_message: "The ipv4_address property must be in the format of WWW.XXX.YYY.ZZZ!",
|
37
|
-
description: "
|
37
|
+
description: "An optional property for the IPv4 address of the printer if it differs from the resource block's name."
|
38
38
|
|
39
39
|
property :port_name, String,
|
40
40
|
description: "The port name."
|
@@ -51,12 +51,12 @@ class Chef
|
|
51
51
|
default: false
|
52
52
|
|
53
53
|
property :port_protocol, Integer,
|
54
|
-
description: "The printer port protocol
|
54
|
+
description: "The printer port protocol: 1 (RAW) or 2 (LPR).",
|
55
55
|
validation_message: "port_protocol must be either 1 for RAW or 2 for LPR!",
|
56
56
|
default: 1, equal_to: [1, 2]
|
57
57
|
|
58
58
|
property :exists, [TrueClass, FalseClass],
|
59
|
-
|
59
|
+
skip_docs: true
|
60
60
|
|
61
61
|
PORTS_REG_KEY = 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Print\Monitors\Standard TCP/IP Port\Ports\\'.freeze unless defined?(PORTS_REG_KEY)
|
62
62
|
|
@@ -50,6 +50,7 @@ class Chef
|
|
50
50
|
# The display name to be used by user interface programs to identify the
|
51
51
|
# service. This string has a maximum length of 256 characters.
|
52
52
|
property :display_name, String, regex: /^.{1,256}$/,
|
53
|
+
validation_message: "The display_name can only be a maximum of 256 characters!",
|
53
54
|
introduced: "14.0"
|
54
55
|
|
55
56
|
# https://github.com/djberg96/win32-service/blob/ffi/lib/win32/windows/constants.rb#L19-L29
|
@@ -0,0 +1,315 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Sölvi Páll Ásgeirsson (<solvip@gmail.com>)
|
3
|
+
# Author:: Richard Lavey (richard.lavey@calastone.com)
|
4
|
+
# Author:: Tim Smith (tsmith@chef.io)
|
5
|
+
#
|
6
|
+
# Copyright:: 2014-2017, Sölvi Páll Ásgeirsson.
|
7
|
+
# Copyright:: 2018, Chef Software, Inc.
|
8
|
+
#
|
9
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
10
|
+
# you may not use this file except in compliance with the License.
|
11
|
+
# You may obtain a copy of the License at
|
12
|
+
#
|
13
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
14
|
+
#
|
15
|
+
# Unless required by applicable law or agreed to in writing, software
|
16
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
17
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
18
|
+
# See the License for the specific language governing permissions and
|
19
|
+
# limitations under the License.
|
20
|
+
#
|
21
|
+
|
22
|
+
require "chef/resource"
|
23
|
+
require "chef/json_compat"
|
24
|
+
|
25
|
+
class Chef
|
26
|
+
class Resource
|
27
|
+
class WindowsShare < Chef::Resource
|
28
|
+
preview_resource true
|
29
|
+
resource_name :windows_share
|
30
|
+
|
31
|
+
description "Use the windows_share resource to create, modify and remove Windows shares."
|
32
|
+
introduced "14.7"
|
33
|
+
|
34
|
+
# 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.
|
35
|
+
property :share_name, String,
|
36
|
+
description: "The name to assign to the share.",
|
37
|
+
name_property: true
|
38
|
+
|
39
|
+
# 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.
|
40
|
+
property :path, String,
|
41
|
+
description: "The path of the folder to share. Required when creating. If the share already exists on a different path then it is deleted and re-created."
|
42
|
+
|
43
|
+
# 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.
|
44
|
+
property :description, String,
|
45
|
+
description: "The description to be applied to the share.",
|
46
|
+
default: ""
|
47
|
+
|
48
|
+
# 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.
|
49
|
+
property :full_users, Array,
|
50
|
+
description: "The users that should have 'Full control' permissions on the share in domain\\username format.",
|
51
|
+
default: lazy { [] }, coerce: proc { |u| u.sort }
|
52
|
+
|
53
|
+
# Specifies which users are granted modify permission to access the share
|
54
|
+
property :change_users, Array,
|
55
|
+
description: "The users that should have 'modify' permission on the share in domain\\username format.",
|
56
|
+
default: lazy { [] }, coerce: proc { |u| u.sort }
|
57
|
+
|
58
|
+
# Specifies which users are granted read permission to access the share. Multiple users can be specified by supplying a comma-separated list.
|
59
|
+
property :read_users, Array,
|
60
|
+
description: "The users that should have 'read' permission on the share in domain\\username format.",
|
61
|
+
default: lazy { [] }, coerce: proc { |u| u.sort }
|
62
|
+
|
63
|
+
# 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.
|
64
|
+
property :temporary, [TrueClass, FalseClass],
|
65
|
+
description: "The lifetime of the new SMB share. A temporary share does not persist beyond the next restart of the computer.",
|
66
|
+
default: false
|
67
|
+
|
68
|
+
# Specifies the scope name of the share.
|
69
|
+
property :scope_name, String,
|
70
|
+
description: "The scope name of the share.",
|
71
|
+
default: "*"
|
72
|
+
|
73
|
+
# Specifies the continuous availability time-out for the share.
|
74
|
+
property :ca_timeout, Integer,
|
75
|
+
description: "The continuous availability time-out for the share.",
|
76
|
+
default: 0
|
77
|
+
|
78
|
+
# Indicates that the share is continuously available.
|
79
|
+
property :continuously_available, [TrueClass, FalseClass],
|
80
|
+
description: "Indicates that the share is continuously available.",
|
81
|
+
default: false
|
82
|
+
|
83
|
+
# Specifies the caching mode of the offline files for the SMB share.
|
84
|
+
# property :caching_mode, String, equal_to: %w(None Manual Documents Programs BranchCache)
|
85
|
+
|
86
|
+
# 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.
|
87
|
+
property :concurrent_user_limit, Integer,
|
88
|
+
description: "The maximum number of concurrently connected users the share can accommodate.",
|
89
|
+
default: 0
|
90
|
+
|
91
|
+
# Indicates that the share is encrypted.
|
92
|
+
property :encrypt_data, [TrueClass, FalseClass],
|
93
|
+
description: "Indicates that the share is encrypted.",
|
94
|
+
default: false
|
95
|
+
|
96
|
+
# 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.
|
97
|
+
# property :folder_enumeration_mode, String, equal_to: %(AccessBased Unrestricted)
|
98
|
+
|
99
|
+
include Chef::Mixin::PowershellOut
|
100
|
+
|
101
|
+
load_current_value do |desired|
|
102
|
+
# this command selects individual objects because EncryptData & CachingMode have underlying
|
103
|
+
# types that get converted to their Integer values by ConvertTo-Json & we need to make sure
|
104
|
+
# those get written out as strings
|
105
|
+
share_state_cmd = "Get-SmbShare -Name '#{desired.share_name}' | Select-Object Name,Path, Description, Temporary, CATimeout, ContinuouslyAvailable, ConcurrentUserLimit, EncryptData | ConvertTo-Json"
|
106
|
+
|
107
|
+
Chef::Log.debug("Running '#{share_state_cmd}' to determine share state'")
|
108
|
+
ps_results = powershell_out(share_state_cmd)
|
109
|
+
|
110
|
+
# detect a failure without raising and then set current_resource to nil
|
111
|
+
if ps_results.error?
|
112
|
+
Chef::Log.debug("Error fetching share state: #{ps_results.stderr}")
|
113
|
+
current_value_does_not_exist!
|
114
|
+
end
|
115
|
+
|
116
|
+
Chef::Log.debug("The Get-SmbShare results were #{ps_results.stdout}")
|
117
|
+
results = Chef::JSONCompat.from_json(ps_results.stdout)
|
118
|
+
|
119
|
+
path results["Path"]
|
120
|
+
description results["Description"]
|
121
|
+
temporary results["Temporary"]
|
122
|
+
ca_timeout results["CATimeout"]
|
123
|
+
continuously_available results["ContinuouslyAvailable"]
|
124
|
+
# caching_mode results['CachingMode']
|
125
|
+
concurrent_user_limit results["ConcurrentUserLimit"]
|
126
|
+
encrypt_data results["EncryptData"]
|
127
|
+
# folder_enumeration_mode results['FolderEnumerationMode']
|
128
|
+
|
129
|
+
perm_state_cmd = %{Get-SmbShareAccess -Name "#{desired.share_name}" | Select-Object AccountName,AccessControlType,AccessRight | ConvertTo-Json}
|
130
|
+
|
131
|
+
Chef::Log.debug("Running '#{perm_state_cmd}' to determine share permissions state'")
|
132
|
+
ps_perm_results = powershell_out(perm_state_cmd)
|
133
|
+
|
134
|
+
# we raise here instead of warning like above because we'd only get here if the above Get-SmbShare
|
135
|
+
# command was successful and that continuing would leave us with 1/2 known state
|
136
|
+
raise "Could not determine #{desired.share_name} share permissions by running '#{perm_state_cmd}'" if ps_perm_results.error?
|
137
|
+
|
138
|
+
Chef::Log.debug("The Get-SmbShareAccess results were #{ps_perm_results.stdout}")
|
139
|
+
|
140
|
+
f_users, c_users, r_users = parse_permissions(ps_perm_results.stdout)
|
141
|
+
|
142
|
+
full_users f_users
|
143
|
+
change_users c_users
|
144
|
+
read_users r_users
|
145
|
+
end
|
146
|
+
|
147
|
+
def after_created
|
148
|
+
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
|
149
|
+
end
|
150
|
+
|
151
|
+
# given the string output of Get-SmbShareAccess parse out
|
152
|
+
# arrays of full access users, change users, and read only users
|
153
|
+
def parse_permissions(results_string)
|
154
|
+
json_results = Chef::JSONCompat.from_json(results_string)
|
155
|
+
json_results = [json_results] unless json_results.is_a?(Array) # single result is not an array
|
156
|
+
|
157
|
+
f_users = []
|
158
|
+
c_users = []
|
159
|
+
r_users = []
|
160
|
+
|
161
|
+
json_results.each do |perm|
|
162
|
+
next unless perm["AccessControlType"] == 0 # allow
|
163
|
+
case perm["AccessRight"]
|
164
|
+
when 0 then f_users << stripped_account(perm["AccountName"]) # 0 full control
|
165
|
+
when 1 then c_users << stripped_account(perm["AccountName"]) # 1 == change
|
166
|
+
when 2 then r_users << stripped_account(perm["AccountName"]) # 2 == read
|
167
|
+
end
|
168
|
+
end
|
169
|
+
[f_users, c_users, r_users]
|
170
|
+
end
|
171
|
+
|
172
|
+
# local names are returned from Get-SmbShareAccess in the full format MACHINE\\NAME
|
173
|
+
# but users of this resource would simply say NAME so we need to strip the values for comparison
|
174
|
+
def stripped_account(name)
|
175
|
+
name.slice!("#{node['hostname']}\\")
|
176
|
+
name
|
177
|
+
end
|
178
|
+
|
179
|
+
action :create do
|
180
|
+
description "Create and modify Windows shares."
|
181
|
+
|
182
|
+
# we do this here instead of requiring the property because :delete doesn't need path set
|
183
|
+
raise "No path property set" unless new_resource.path
|
184
|
+
|
185
|
+
converge_if_changed do
|
186
|
+
# you can't actually change the path so you have to delete the old share first
|
187
|
+
delete_share if different_path?
|
188
|
+
|
189
|
+
# powershell cmdlet for create is different than updates
|
190
|
+
if current_resource.nil?
|
191
|
+
Chef::Log.debug("The current resource is nil so we will create a new share")
|
192
|
+
create_share
|
193
|
+
else
|
194
|
+
Chef::Log.debug("The current resource was not nil so we will update an existing share")
|
195
|
+
update_share
|
196
|
+
end
|
197
|
+
|
198
|
+
# creating the share does not set permissions so we need to update
|
199
|
+
update_permissions
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
action :delete do
|
204
|
+
description "Delete an existing Windows share."
|
205
|
+
|
206
|
+
if current_resource.nil?
|
207
|
+
Chef::Log.debug("#{new_resource.share_name} does not exist - nothing to do")
|
208
|
+
else
|
209
|
+
converge_by("delete #{new_resource.share_name}") do
|
210
|
+
delete_share
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
action_class do
|
216
|
+
def different_path?
|
217
|
+
return false if current_resource.nil? # going from nil to something isn't different for our concerns
|
218
|
+
return false if current_resource.path == new_resource.path
|
219
|
+
true
|
220
|
+
end
|
221
|
+
|
222
|
+
def delete_share
|
223
|
+
delete_command = "Remove-SmbShare -Name '#{new_resource.share_name}' -Force"
|
224
|
+
|
225
|
+
Chef::Log.debug("Running '#{delete_command}' to remove the share")
|
226
|
+
powershell_out!(delete_command)
|
227
|
+
end
|
228
|
+
|
229
|
+
def update_share
|
230
|
+
update_command = "Set-SmbShare -Name '#{new_resource.share_name}' -Description '#{new_resource.description}' -Force"
|
231
|
+
|
232
|
+
Chef::Log.debug("Running '#{update_command}' to update the share")
|
233
|
+
powershell_out!(update_command)
|
234
|
+
end
|
235
|
+
|
236
|
+
def create_share
|
237
|
+
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
|
238
|
+
|
239
|
+
share_cmd = "New-SmbShare -Name '#{new_resource.share_name}' -Path '#{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)}"
|
240
|
+
share_cmd << " -ScopeName #{new_resource.scope_name}" unless new_resource.scope_name == "*" # passing * causes the command to fail
|
241
|
+
share_cmd << " -Temporary:#{bool_string(new_resource.temporary)}" if new_resource.temporary # only set true
|
242
|
+
|
243
|
+
Chef::Log.debug("Running '#{share_cmd}' to create the share")
|
244
|
+
powershell_out!(share_cmd)
|
245
|
+
end
|
246
|
+
|
247
|
+
# determine what users in the current state don't exist in the desired state
|
248
|
+
# users/groups will have their permissions updated with the same command that
|
249
|
+
# sets it, but removes must be performed with Revoke-SmbShareAccess
|
250
|
+
def users_to_revoke
|
251
|
+
@users_to_revoke ||= begin
|
252
|
+
# if the resource doesn't exist then nothing needs to be revoked
|
253
|
+
if current_resource.nil?
|
254
|
+
[]
|
255
|
+
else # if it exists then calculate the current to new resource diffs
|
256
|
+
(current_resource.full_users + current_resource.change_users + current_resource.read_users) - (new_resource.full_users + new_resource.change_users + new_resource.read_users)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
# update existing permissions on a share
|
262
|
+
def update_permissions
|
263
|
+
# revoke any users that had something, but now has nothing
|
264
|
+
revoke_user_permissions(users_to_revoke) unless users_to_revoke.empty?
|
265
|
+
|
266
|
+
# set permissions for each of the permission types
|
267
|
+
%w{full read change}.each do |perm_type|
|
268
|
+
# set permissions for a brand new share OR
|
269
|
+
# update permissions if the current state and desired state differ
|
270
|
+
next unless permissions_need_update?(perm_type)
|
271
|
+
grant_command = "Grant-SmbShareAccess -Name '#{new_resource.share_name}' -AccountName \"#{new_resource.send("#{perm_type}_users").join('","')}\" -Force -AccessRight #{perm_type}"
|
272
|
+
|
273
|
+
Chef::Log.debug("Running '#{grant_command}' to update the share permissions")
|
274
|
+
powershell_out!(grant_command)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
# determine if permissions need to be updated.
|
279
|
+
# Brand new share with no permissions defined: no
|
280
|
+
# Brand new share with permissions defined: yes
|
281
|
+
# Existing share with differing permissions: yes
|
282
|
+
#
|
283
|
+
# @param [String] type the permissions type (Full, Read, or Change)
|
284
|
+
def permissions_need_update?(type)
|
285
|
+
property_name = "#{type}_users"
|
286
|
+
|
287
|
+
# brand new share, but nothing to set
|
288
|
+
return false if current_resource.nil? && new_resource.send(property_name).empty?
|
289
|
+
|
290
|
+
# brand new share with new permissions to set
|
291
|
+
return true if current_resource.nil? && !new_resource.send(property_name).empty?
|
292
|
+
|
293
|
+
# there's a difference between the current and desired state
|
294
|
+
return true unless (new_resource.send(property_name) - current_resource.send(property_name)).empty?
|
295
|
+
|
296
|
+
# anything else
|
297
|
+
false
|
298
|
+
end
|
299
|
+
|
300
|
+
def revoke_user_permissions(users)
|
301
|
+
revoke_command = "Revoke-SmbShareAccess -Name '#{new_resource.share_name}' -AccountName \"#{users.join(',')}\" -Force"
|
302
|
+
Chef::Log.debug("Running '#{revoke_command}' to revoke share permissions")
|
303
|
+
powershell_out!(revoke_command)
|
304
|
+
end
|
305
|
+
|
306
|
+
# convert True/False into "$True" & "$False"
|
307
|
+
def bool_string(bool)
|
308
|
+
# bool ? 1 : 0
|
309
|
+
bool ? "$true" : "$false"
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|