From 38b20d0c5bebe3a57d2823d65d717a008c36e1e1 Mon Sep 17 00:00:00 2001 From: Tim Smith Date: Tue, 13 Mar 2018 16:34:41 -0700 Subject: Add windows_adjoin resource Copied with permission from John Snow's win_ad cookbook. It's been reworked to not mess with dns servers and use powershell_out + sensitive. Some of those changes are getting pushed upstream, but the name is different and there's no expectation of compatibility here. Signed-off-by: Tim Smith --- lib/chef/resource/windows_adjoin.rb | 80 ++++++++++++++++++++++++++++++++++++ lib/chef/resources.rb | 1 + spec/unit/resource/windows_adjoin.rb | 34 +++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 lib/chef/resource/windows_adjoin.rb create mode 100644 spec/unit/resource/windows_adjoin.rb diff --git a/lib/chef/resource/windows_adjoin.rb b/lib/chef/resource/windows_adjoin.rb new file mode 100644 index 0000000000..c66aae9392 --- /dev/null +++ b/lib/chef/resource/windows_adjoin.rb @@ -0,0 +1,80 @@ +# +# Author:: John Snow () +# Copyright:: 2016-2018, John Snow +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "chef/resource" +require "chef/mixin/powershell_out" + +class Chef + class Resource + class WindowsAdJoin < Chef::Resource + resource_name :windows_adjoin + provides :windows_adjoin + + include Chef::Mixin::PowershellOut + + description "Use the windows_adjoin resource to join a Windows Active Directory domain." + introduced "14.0" + + property :domain_name, String, + description: "The domain name to join.", + name_property: true + + property :domain_user, String, + description: "The domain user to use to join the host to the domain.", + required: true + + property :domain_password, String, + description: "The password for the domain user.", + required: true + + property :ou_path, String, + description: "The path to the OU where you would like to place the host." + + property :restart, [TrueClass, FalseClass], + description: "Restart the system after joining the domain. This is necessary for changes to take effect.", + default: true + + # define this again so we can default it to true. Otherwise failures print the password + property :sensitive, [TrueClass, FalseClass], + default: true + + action :join do + unless on_domain? + cmd = "$pswd = ConvertTo-SecureString \'#{new_resource.domain_password}\' -AsPlainText -Force;" + cmd << "$credential = New-Object System.Management.Automation.PSCredential (\"#{new_resource.domain_user}\",$pswd);" + cmd << "Add-Computer -DomainName #{new_resource.domain_name} -Credential $credential" + cmd << " -OUPath \"#{new_resource.ou_path}\"" if new_resource.ou_path + cmd << " -Restart" if new_resource.restart + cmd << " -Force" + + converge_by("join Active Directory domain #{new_resource.domain_name}") do + ps_run = powershell_out(cmd) + raise "Failed to join the domain #{new_resource.domain_name}: #{ps_run.stderr}}" if ps_run.error? + end + end + end + + action_class do + def on_domain? + node_domain = powershell_out!('(Get-WmiObject Win32_ComputerSystem).Domain') + raise "Failed to check if the system is joined to the domain #{new_resource.domain_name}: #{node_domain.stderr}}" if node_domain.error? + node_domain.stdout.downcase.strip == new_resource.domain_name.downcase + end + end + end + end +end diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb index a62cb22c8c..f5e113054b 100644 --- a/lib/chef/resources.rb +++ b/lib/chef/resources.rb @@ -114,6 +114,7 @@ require "chef/resource/zypper_repository" require "chef/resource/cab_package" require "chef/resource/powershell_package" require "chef/resource/msu_package" +require "chef/resource/windows_adjoin" require "chef/resource/windows_auto_run" require "chef/resource/windows_feature" require "chef/resource/windows_feature_dism" diff --git a/spec/unit/resource/windows_adjoin.rb b/spec/unit/resource/windows_adjoin.rb new file mode 100644 index 0000000000..4ce20d5202 --- /dev/null +++ b/spec/unit/resource/windows_adjoin.rb @@ -0,0 +1,34 @@ +# +# Copyright:: Copyright 2018, Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "spec_helper" + +describe Chef::Resource::WindowsAdJoin do + let(:resource) { Chef::Resource::WindowsAdJoin.new("example.com") } + + it "sets resource name as :windows_adjoin" do + expect(resource.resource_name).to eql(:windows_adjoin) + end + + it "sets the domain_name as its name" do + expect(resource.domain_name).to eql("example.com") + end + + it "sets the default action as :join" do + expect(resource.action).to eql([:join]) + end +end -- cgit v1.2.1 From 63fc9a59b870b1890ed658056d04619467c66710 Mon Sep 17 00:00:00 2001 From: Tim Smith Date: Thu, 22 Mar 2018 14:40:07 -0700 Subject: Handle reboots in Chef and provide finer grained control of behavior Rename the property to reboot to align with Chef naming. Allow delayed, immediate, or never for reboot actions. Using the actions from reboot was pretty confusing so I used notification names which seems to lineup nicely. Signed-off-by: Tim Smith --- lib/chef/resource/windows_adjoin.rb | 18 +++++++++++++----- spec/unit/resource/windows_adjoin.rb | 7 +++++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/lib/chef/resource/windows_adjoin.rb b/lib/chef/resource/windows_adjoin.rb index c66aae9392..25e2537c03 100644 --- a/lib/chef/resource/windows_adjoin.rb +++ b/lib/chef/resource/windows_adjoin.rb @@ -44,9 +44,11 @@ class Chef property :ou_path, String, description: "The path to the OU where you would like to place the host." - property :restart, [TrueClass, FalseClass], - description: "Restart the system after joining the domain. This is necessary for changes to take effect.", - default: true + property :reboot, Symbol, + equal_to: [:immediate, :delayed, :never], + validation_message: "The reboot property accepts :immediate (reboot as soon as the resource completes), :delayed (reboot once the Chef run completes), and :never (Don't reboot)" + description: "Controls the system reboot behavior post domain joining. Reboot immediately, after the Chef run completes, or never. Note that a reboot is necessary for changes to take effect.", + default: :immediate # define this again so we can default it to true. Otherwise failures print the password property :sensitive, [TrueClass, FalseClass], @@ -58,19 +60,25 @@ class Chef cmd << "$credential = New-Object System.Management.Automation.PSCredential (\"#{new_resource.domain_user}\",$pswd);" cmd << "Add-Computer -DomainName #{new_resource.domain_name} -Credential $credential" cmd << " -OUPath \"#{new_resource.ou_path}\"" if new_resource.ou_path - cmd << " -Restart" if new_resource.restart cmd << " -Force" converge_by("join Active Directory domain #{new_resource.domain_name}") do ps_run = powershell_out(cmd) raise "Failed to join the domain #{new_resource.domain_name}: #{ps_run.stderr}}" if ps_run.error? + + unless new_resource.reboot == :never + declare_resource(:reboot, "Reboot to join domain #{new_resource.domain_name}") do + action new_resource.reboot + reason "Reboot to join domain #{new_resource.domain_name}" + end + end end end end action_class do def on_domain? - node_domain = powershell_out!('(Get-WmiObject Win32_ComputerSystem).Domain') + node_domain = powershell_out!("(Get-WmiObject Win32_ComputerSystem).Domain") raise "Failed to check if the system is joined to the domain #{new_resource.domain_name}: #{node_domain.stderr}}" if node_domain.error? node_domain.stdout.downcase.strip == new_resource.domain_name.downcase end diff --git a/spec/unit/resource/windows_adjoin.rb b/spec/unit/resource/windows_adjoin.rb index 4ce20d5202..bbaec7eaa6 100644 --- a/spec/unit/resource/windows_adjoin.rb +++ b/spec/unit/resource/windows_adjoin.rb @@ -31,4 +31,11 @@ describe Chef::Resource::WindowsAdJoin do it "sets the default action as :join" do expect(resource.action).to eql([:join]) end + + it "accepts :immediate, :delayed, or :never values for 'reboot' property" do + expect { resource.reboot :immediate }.not_to raise_error + expect { resource.reboot :delayed }.not_to raise_error + expect { resource.reboot :never }.not_to raise_error + expect { resource.reboot :nopenope }.to raise_error(ArgumentError) + end end -- cgit v1.2.1 From 7b7a918a0b60e6c1bc7e5da6947db453f7b6dc4a Mon Sep 17 00:00:00 2001 From: Tim Smith Date: Thu, 22 Mar 2018 14:46:41 -0700 Subject: Require the domain_name to be a FQDN Signed-off-by: Tim Smith --- lib/chef/resource/windows_adjoin.rb | 6 ++++-- spec/unit/resource/windows_adjoin.rb | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/chef/resource/windows_adjoin.rb b/lib/chef/resource/windows_adjoin.rb index 25e2537c03..edc2effe26 100644 --- a/lib/chef/resource/windows_adjoin.rb +++ b/lib/chef/resource/windows_adjoin.rb @@ -30,7 +30,9 @@ class Chef introduced "14.0" property :domain_name, String, - description: "The domain name to join.", + description: "The FQDN of the AD domain to join.", + validation_message: "The 'domain_name' property must be a FQDN.", + regex: /.\../, # anything.anything name_property: true property :domain_user, String, @@ -46,7 +48,7 @@ class Chef property :reboot, Symbol, equal_to: [:immediate, :delayed, :never], - validation_message: "The reboot property accepts :immediate (reboot as soon as the resource completes), :delayed (reboot once the Chef run completes), and :never (Don't reboot)" + validation_message: "The reboot property accepts :immediate (reboot as soon as the resource completes), :delayed (reboot once the Chef run completes), and :never (Don't reboot)", description: "Controls the system reboot behavior post domain joining. Reboot immediately, after the Chef run completes, or never. Note that a reboot is necessary for changes to take effect.", default: :immediate diff --git a/spec/unit/resource/windows_adjoin.rb b/spec/unit/resource/windows_adjoin.rb index bbaec7eaa6..02975d5d4e 100644 --- a/spec/unit/resource/windows_adjoin.rb +++ b/spec/unit/resource/windows_adjoin.rb @@ -28,6 +28,10 @@ describe Chef::Resource::WindowsAdJoin do expect(resource.domain_name).to eql("example.com") end + it "only accepts FQDNs for the domain_name property" do + expect { resource.domain_name "example" }.to raise_error(ArgumentError) + end + it "sets the default action as :join" do expect(resource.action).to eql([:join]) end -- cgit v1.2.1 From cee8fed38987f280028df9f334583143a78d46b1 Mon Sep 17 00:00:00 2001 From: Tim Smith Date: Thu, 22 Mar 2018 15:34:35 -0700 Subject: Rename to windows_ad_join This allows us to have windows_ad_* in the future Signed-off-by: Tim Smith --- lib/chef/resource/windows_ad_join.rb | 90 +++++++++++++++++++++++++++++++++++ lib/chef/resource/windows_adjoin.rb | 90 ----------------------------------- lib/chef/resources.rb | 2 +- spec/unit/resource/windows_ad_join.rb | 45 ++++++++++++++++++ spec/unit/resource/windows_adjoin.rb | 45 ------------------ 5 files changed, 136 insertions(+), 136 deletions(-) create mode 100644 lib/chef/resource/windows_ad_join.rb delete mode 100644 lib/chef/resource/windows_adjoin.rb create mode 100644 spec/unit/resource/windows_ad_join.rb delete mode 100644 spec/unit/resource/windows_adjoin.rb diff --git a/lib/chef/resource/windows_ad_join.rb b/lib/chef/resource/windows_ad_join.rb new file mode 100644 index 0000000000..b0f66cf2c0 --- /dev/null +++ b/lib/chef/resource/windows_ad_join.rb @@ -0,0 +1,90 @@ +# +# Author:: John Snow () +# Copyright:: 2016-2018, John Snow +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "chef/resource" +require "chef/mixin/powershell_out" + +class Chef + class Resource + class WindowsAdJoin < Chef::Resource + resource_name :windows_ad_join + provides :windows_ad_join + + include Chef::Mixin::PowershellOut + + description "Use the windows_ad_join resource to join a Windows Active Directory domain." + introduced "14.0" + + property :domain_name, String, + description: "The FQDN of the AD domain to join.", + validation_message: "The 'domain_name' property must be a FQDN.", + regex: /.\../, # anything.anything + name_property: true + + property :domain_user, String, + description: "The domain user to use to join the host to the domain.", + required: true + + property :domain_password, String, + description: "The password for the domain user.", + required: true + + property :ou_path, String, + description: "The path to the OU where you would like to place the host." + + property :reboot, Symbol, + equal_to: [:immediate, :delayed, :never], + validation_message: "The reboot property accepts :immediate (reboot as soon as the resource completes), :delayed (reboot once the Chef run completes), and :never (Don't reboot)", + description: "Controls the system reboot behavior post domain joining. Reboot immediately, after the Chef run completes, or never. Note that a reboot is necessary for changes to take effect.", + default: :immediate + + # define this again so we can default it to true. Otherwise failures print the password + property :sensitive, [TrueClass, FalseClass], + default: true + + action :join do + unless on_domain? + cmd = "$pswd = ConvertTo-SecureString \'#{new_resource.domain_password}\' -AsPlainText -Force;" + cmd << "$credential = New-Object System.Management.Automation.PSCredential (\"#{new_resource.domain_user}\",$pswd);" + cmd << "Add-Computer -DomainName #{new_resource.domain_name} -Credential $credential" + cmd << " -OUPath \"#{new_resource.ou_path}\"" if new_resource.ou_path + cmd << " -Force" + + converge_by("join Active Directory domain #{new_resource.domain_name}") do + ps_run = powershell_out(cmd) + raise "Failed to join the domain #{new_resource.domain_name}: #{ps_run.stderr}}" if ps_run.error? + + unless new_resource.reboot == :never + declare_resource(:reboot, "Reboot to join domain #{new_resource.domain_name}") do + action new_resource.reboot + reason "Reboot to join domain #{new_resource.domain_name}" + end + end + end + end + end + + action_class do + def on_domain? + node_domain = powershell_out!("(Get-WmiObject Win32_ComputerSystem).Domain") + raise "Failed to check if the system is joined to the domain #{new_resource.domain_name}: #{node_domain.stderr}}" if node_domain.error? + node_domain.stdout.downcase.strip == new_resource.domain_name.downcase + end + end + end + end +end diff --git a/lib/chef/resource/windows_adjoin.rb b/lib/chef/resource/windows_adjoin.rb deleted file mode 100644 index edc2effe26..0000000000 --- a/lib/chef/resource/windows_adjoin.rb +++ /dev/null @@ -1,90 +0,0 @@ -# -# Author:: John Snow () -# Copyright:: 2016-2018, John Snow -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -require "chef/resource" -require "chef/mixin/powershell_out" - -class Chef - class Resource - class WindowsAdJoin < Chef::Resource - resource_name :windows_adjoin - provides :windows_adjoin - - include Chef::Mixin::PowershellOut - - description "Use the windows_adjoin resource to join a Windows Active Directory domain." - introduced "14.0" - - property :domain_name, String, - description: "The FQDN of the AD domain to join.", - validation_message: "The 'domain_name' property must be a FQDN.", - regex: /.\../, # anything.anything - name_property: true - - property :domain_user, String, - description: "The domain user to use to join the host to the domain.", - required: true - - property :domain_password, String, - description: "The password for the domain user.", - required: true - - property :ou_path, String, - description: "The path to the OU where you would like to place the host." - - property :reboot, Symbol, - equal_to: [:immediate, :delayed, :never], - validation_message: "The reboot property accepts :immediate (reboot as soon as the resource completes), :delayed (reboot once the Chef run completes), and :never (Don't reboot)", - description: "Controls the system reboot behavior post domain joining. Reboot immediately, after the Chef run completes, or never. Note that a reboot is necessary for changes to take effect.", - default: :immediate - - # define this again so we can default it to true. Otherwise failures print the password - property :sensitive, [TrueClass, FalseClass], - default: true - - action :join do - unless on_domain? - cmd = "$pswd = ConvertTo-SecureString \'#{new_resource.domain_password}\' -AsPlainText -Force;" - cmd << "$credential = New-Object System.Management.Automation.PSCredential (\"#{new_resource.domain_user}\",$pswd);" - cmd << "Add-Computer -DomainName #{new_resource.domain_name} -Credential $credential" - cmd << " -OUPath \"#{new_resource.ou_path}\"" if new_resource.ou_path - cmd << " -Force" - - converge_by("join Active Directory domain #{new_resource.domain_name}") do - ps_run = powershell_out(cmd) - raise "Failed to join the domain #{new_resource.domain_name}: #{ps_run.stderr}}" if ps_run.error? - - unless new_resource.reboot == :never - declare_resource(:reboot, "Reboot to join domain #{new_resource.domain_name}") do - action new_resource.reboot - reason "Reboot to join domain #{new_resource.domain_name}" - end - end - end - end - end - - action_class do - def on_domain? - node_domain = powershell_out!("(Get-WmiObject Win32_ComputerSystem).Domain") - raise "Failed to check if the system is joined to the domain #{new_resource.domain_name}: #{node_domain.stderr}}" if node_domain.error? - node_domain.stdout.downcase.strip == new_resource.domain_name.downcase - end - end - end - end -end diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb index f5e113054b..e123cc5915 100644 --- a/lib/chef/resources.rb +++ b/lib/chef/resources.rb @@ -114,7 +114,7 @@ require "chef/resource/zypper_repository" require "chef/resource/cab_package" require "chef/resource/powershell_package" require "chef/resource/msu_package" -require "chef/resource/windows_adjoin" +require "chef/resource/windows_ad_join" require "chef/resource/windows_auto_run" require "chef/resource/windows_feature" require "chef/resource/windows_feature_dism" diff --git a/spec/unit/resource/windows_ad_join.rb b/spec/unit/resource/windows_ad_join.rb new file mode 100644 index 0000000000..7fd02c2021 --- /dev/null +++ b/spec/unit/resource/windows_ad_join.rb @@ -0,0 +1,45 @@ +# +# Copyright:: Copyright 2018, Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "spec_helper" + +describe Chef::Resource::WindowsAdJoin do + let(:resource) { Chef::Resource::WindowsAdJoin.new("example.com") } + + it "sets resource name as :windows_ad_join" do + expect(resource.resource_name).to eql(:windows_ad_join) + end + + it "sets the domain_name as its name" do + expect(resource.domain_name).to eql("example.com") + end + + it "only accepts FQDNs for the domain_name property" do + expect { resource.domain_name "example" }.to raise_error(ArgumentError) + end + + it "sets the default action as :join" do + expect(resource.action).to eql([:join]) + end + + it "accepts :immediate, :delayed, or :never values for 'reboot' property" do + expect { resource.reboot :immediate }.not_to raise_error + expect { resource.reboot :delayed }.not_to raise_error + expect { resource.reboot :never }.not_to raise_error + expect { resource.reboot :nopenope }.to raise_error(ArgumentError) + end +end diff --git a/spec/unit/resource/windows_adjoin.rb b/spec/unit/resource/windows_adjoin.rb deleted file mode 100644 index 02975d5d4e..0000000000 --- a/spec/unit/resource/windows_adjoin.rb +++ /dev/null @@ -1,45 +0,0 @@ -# -# Copyright:: Copyright 2018, Chef Software, Inc. -# License:: Apache License, Version 2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -require "spec_helper" - -describe Chef::Resource::WindowsAdJoin do - let(:resource) { Chef::Resource::WindowsAdJoin.new("example.com") } - - it "sets resource name as :windows_adjoin" do - expect(resource.resource_name).to eql(:windows_adjoin) - end - - it "sets the domain_name as its name" do - expect(resource.domain_name).to eql("example.com") - end - - it "only accepts FQDNs for the domain_name property" do - expect { resource.domain_name "example" }.to raise_error(ArgumentError) - end - - it "sets the default action as :join" do - expect(resource.action).to eql([:join]) - end - - it "accepts :immediate, :delayed, or :never values for 'reboot' property" do - expect { resource.reboot :immediate }.not_to raise_error - expect { resource.reboot :delayed }.not_to raise_error - expect { resource.reboot :never }.not_to raise_error - expect { resource.reboot :nopenope }.to raise_error(ArgumentError) - end -end -- cgit v1.2.1