summaryrefslogtreecommitdiff
path: root/lib/chef/resource/windows_feature_powershell.rb
blob: 7e7fb6c16077ccf63eb84680a796321efe4e932d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#
# Author:: Greg Zapp (<greg.zapp@gmail.com>)
#
# Copyright:: 2015-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/resource"

class Chef
  class Resource
    class WindowsFeaturePowershell < Chef::Resource
      resource_name :windows_feature_powershell
      provides :windows_feature_powershell

      description "Use the windows_feature_powershell resource to add, remove or"\
                  " 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"
      introduced "14.0"

      property :feature_name, [Array, String],
               description: "The name of the feature/role(s) to install if it differs from the resource name.",
               coerce: proc { |x| Array(x) },
               name_property: true

      property :source, String,
               description: "Use a local repository for the feature install."

      property :all, [TrueClass, FalseClass],
               description: "Install all sub features. This is equivalent to using the"\
                            " -InstallAllSubFeatures switch with Add-WindowsFeature.",
               default: false

      property :timeout, Integer,
               description: "Specifies a timeout (in seconds) for feature install.",
               default: 600

      property :management_tools, [TrueClass, FalseClass],
               description: "",
               default: false

      include Chef::Mixin::PowershellOut

      action :install do
        description "Install a Windows role/feature using PowerShell"

        Chef::Log.warn("Requested feature #{new_resource.feature_name.join(',')} is not available on this system.") unless available?
        unless !available? || installed?
          converge_by("install Windows feature#{'s' if new_resource.feature_name.count > 1} #{new_resource.feature_name.join(',')}") do
            addsource = new_resource.source ? "-Source \"#{new_resource.source}\"" : ""
            addall = new_resource.all ? "-IncludeAllSubFeature" : ""
            addmanagementtools = new_resource.management_tools ? "-IncludeManagementTools" : ""
            cmd = if node["os_version"].to_f < 6.2
                    powershell_out!("#{install_feature_cmdlet} #{new_resource.feature_name.join(',')} #{addall}", timeout: new_resource.timeout)
                  else
                    powershell_out!("#{install_feature_cmdlet} #{new_resource.feature_name.join(',')} #{addsource} #{addall} #{addmanagementtools}", timeout: new_resource.timeout)
                  end
            Chef::Log.info(cmd.stdout)
          end
        end
      end

      action :remove do
        description "Remove a Windows role/feature using PowerShell"

        if installed?
          converge_by("remove Windows feature#{'s' if new_resource.feature_name.count > 1} #{new_resource.feature_name.join(',')}") do
            cmd = powershell_out!("#{remove_feature_cmdlet} #{new_resource.feature_name.join(',')}", timeout: new_resource.timeout)
            Chef::Log.info(cmd.stdout)
          end
        end
      end

      action :delete do
        description "Remove a Windows role/feature from the image using Powershell"

        if available?
          converge_by("delete Windows feature#{'s' if new_resource.feature_name.count > 1} #{new_resource.feature_name.join(',')} from the image") do
            cmd = powershell_out!("Uninstall-WindowsFeature #{new_resource.feature_name.join(',')} -Remove", timeout: new_resource.timeout)
            Chef::Log.info(cmd.stdout)
          end
        end
      end

      action_class do
        # @todo remove this when we're ready to drop windows 8/2012
        def install_feature_cmdlet
          node["os_version"].to_f < 6.2 ? "Import-Module ServerManager; Add-WindowsFeature" : "Install-WindowsFeature"
        end

        # @todo remove this when we're ready to drop windows 8/2012
        def remove_feature_cmdlet
          node["os_version"].to_f < 6.2 ? "Import-Module ServerManager; Remove-WindowsFeature" : "Uninstall-WindowsFeature"
        end

        def installed?
          @installed ||= begin
            # @todo remove this when we're ready to drop windows 8/2012
            cmd = if node["os_version"].to_f < 6.2
                    powershell_out("Import-Module ServerManager; @(Get-WindowsFeature #{new_resource.feature_name.join(',')} | ?{$_.Installed -ne $TRUE}).count", timeout: new_resource.timeout)
                  else
                    powershell_out("@(Get-WindowsFeature #{new_resource.feature_name.join(',')} | ?{$_.InstallState -ne \'Installed\'}).count", timeout: new_resource.timeout)
                  end
            cmd.stderr.empty? && cmd.stdout.chomp.to_i == 0
          end
        end

        def available?
          @available ||= begin
            # @todo remove this when we're ready to drop windows 8/2012
            cmd = if node["os_version"].to_f < 6.2
                    powershell_out("Import-Module ServerManager; @(Get-WindowsFeature #{new_resource.feature_name.join(',')}).count", timeout: new_resource.timeout)
                  else
                    powershell_out("@(Get-WindowsFeature #{new_resource.feature_name.join(',')} | ?{$_.InstallState -ne \'Removed\'}).count", timeout: new_resource.timeout)
                  end
            cmd.stderr.empty? && cmd.stdout.chomp.to_i > 0
          end
        end
      end
    end
  end
end