31 May 2016

PowerShell: Set Windows Features with Verification

I am in the beginning stages of creating a Windows 10 build. One of the first things I needed to do was to install and set the Windows 10 features. Before, I used a batch script that executed DISM to set each feature. I know there is the Install-WindowsFeatures cmdlet, but I also wanted to incorporate verification and other features into a single script. With the help of Sapien's PowerShell Studio, this script was a breeze to write.

This script allows you to set windows features while also verifying each feature was set correctly by querying the feature for the status. It then outputs the feature name and status to the display. I have also included the option to run a report of all available features and their state. Here are the four features the script provides:

  1. Set an individual feature via command line:
    powershell.exe -executionpolicy bypass -command WindowsFeatures.ps1 -Feature 'RSATClient-Features' -Setting 'disable'
  2. Set multiple features by reading a text file located in the same directory as the script. You can name the text file any name you want. The format for the file is: RSATClient,enable for example. Here is the command line:
    powershell.exe -executionpolicy bypass -command WindowsFeatures.ps1 -FeaturesFile 'FeaturesList.txt'

  3. Hard code a feature setting at the bottom of the script:
    Set-WindowsFeature -Name 'RSATClient-Features' -State 'disable'
  4. Display a list of windows features:
    powershell.exe -executionpolicy bypass -command WindowsFeatures.ps1 -ListFeatures $true


You will need to use the -command when executing this at the command line instead of -file. This is because the -ListFeatures is a boolean value. I have also included code that identifies an error 50 and returns a status that you must include the parent feature before activating the specified feature. I have also made the additional command line window be minimized when running the DISM.exe. 

You can download the script from here.
1:  <#  
2:       .SYNOPSIS  
3:            Process Windows Features  
4:         
5:       .DESCRIPTION  
6:            This script can return a list of online windows features and/or set specific windows features.  
7:         
8:       .PARAMETER ListFeatures  
9:            Return a list of all Windows Features  
10:         
11:       .PARAMETER Feature  
12:            A description of the Feature parameter.  
13:         
14:       .PARAMETER Setting  
15:            A description of the Setting parameter.  
16:         
17:       .PARAMETER FeaturesFile  
18:            Name of the features file that contains a list of features with their corresponding settings for this script to process through. The files resides in the same directory as this script.  
19:         
20:       .EXAMPLE  
21:            Return a list of all available online Windows Features  
22:            powershell.exe -executionpolicy bypass -command WindowsFeatures.ps1 -ListFeatures $true  
23:              
24:            Set one Windows Feature from the command line  
25:            powershell.exe -executionpolicy bypass -command WindowsFeatures.ps1 -Feature 'RSATClient-Features' -Setting 'disable'  
26:              
27:            Set multiple features by reading contents of a text file  
28:            powershell.exe -executionpolicy bypass -command WindowsFeatures.ps1 -FeaturesFile 'FeaturesList.txt'  
29:         
30:       .NOTES  
31:            You must use -command instead of -file in the command line because of the use of boolean parameters  
32:    
33:            An error code 50 means you are trying to enable a feature in which the required parent feature is disabled  
34:              
35:            I have also included two commented out lines at the bottom of the script as examples if you want to hardcode the features within the script.  
36:            ===========================================================================  
37:            Created with:     SAPIEN Technologies, Inc., PowerShell Studio 2016 v5.2.122  
38:            Created on:       5/27/2016 2:46 PM  
39:            Created by:       Mick Pletcher  
40:            Organization:  
41:            Filename:         WindowsFeatures.ps1  
42:            ===========================================================================  
43:  #>  
44:  [CmdletBinding()]  
45:  param  
46:  (  
47:            [boolean]$ListFeatures = $false,  
48:            [string]$Feature,  
49:            [ValidateSet('enable', 'disable')][string]$Setting,  
50:            [String]$FeaturesFile  
51:  )  
52:    
53:  function Confirm-Feature {  
54:  <#  
55:       .SYNOPSIS  
56:            Confirm the feature setting  
57:         
58:       .DESCRIPTION  
59:            Confirm the desired change took place for a feature  
60:         
61:       .PARAMETER FeatureName  
62:            Name of the feature  
63:         
64:       .PARAMETER FeatureState  
65:            Desired state of the feature  
66:         
67:       .EXAMPLE  
68:            PS C:\> Confirm-Feature  
69:         
70:       .NOTES  
71:            Additional information about the function.  
72:  #>  
73:         
74:       [CmdletBinding()][OutputType([boolean])]  
75:       param  
76:       (  
77:                 [ValidateNotNull()][string]$FeatureName,  
78:                 [ValidateSet('Enable', 'Disable')][string]$FeatureState  
79:       )  
80:         
81:       $WindowsFeatures = Get-WindowsFeaturesList  
82:       $WindowsFeature = $WindowsFeatures | Where-Object { $_.Name -eq $FeatureName }  
83:       switch ($FeatureState) {  
84:            'Enable' {  
85:                 If (($WindowsFeature.State -eq 'Enabled') -or ($WindowsFeature.State -eq 'Enable Pending')) {  
86:                      Return $true  
87:                 } else {  
88:                      Return $false  
89:                 }  
90:            }  
91:            'Disable' {  
92:                 If (($WindowsFeature.State -eq 'Disabled') -or ($WindowsFeature.State -eq 'Disable Pending')) {  
93:                      Return $true  
94:                 } else {  
95:                      Return $false  
96:                 }  
97:            }  
98:            default {  
99:                 Return $false  
100:            }  
101:       }  
102:         
103:  }  
104:    
105:  function Get-WindowsFeaturesList {  
106:  <#  
107:       .SYNOPSIS  
108:            List Windows Features  
109:         
110:       .DESCRIPTION  
111:            This will list all available online windows features  
112:         
113:       .NOTES  
114:            Additional information about the function.  
115:  #>  
116:         
117:       [CmdletBinding()]  
118:       param ()  
119:         
120:       $Temp = dism /online /get-features  
121:       $Temp = $Temp | Where-Object { ($_ -like '*Feature Name*') -or ($_ -like '*State*') }  
122:       $i = 0  
123:       $Features = @()  
124:       Do {  
125:            $FeatureName = $Temp[$i]  
126:            $FeatureName = $FeatureName.Split(':')  
127:            $FeatureName = $FeatureName[1].Trim()  
128:            $i++  
129:            $FeatureState = $Temp[$i]  
130:            $FeatureState = $FeatureState.Split(':')  
131:            $FeatureState = $FeatureState[1].Trim()  
132:            $Feature = New-Object PSObject  
133:            $Feature | Add-Member noteproperty Name $FeatureName  
134:            $Feature | Add-Member noteproperty State $FeatureState  
135:            $Features += $Feature  
136:            $i++  
137:       } while ($i -lt $Temp.Count)  
138:       $Features = $Features | Sort-Object Name  
139:       Return $Features  
140:  }  
141:    
142:  function Set-WindowsFeature {  
143:  <#  
144:       .SYNOPSIS  
145:            Configure a Windows Feature  
146:         
147:       .DESCRIPTION  
148:            Enable or disable a windows feature  
149:         
150:       .PARAMETER Name  
151:            Name of the windows feature  
152:         
153:       .PARAMETER State  
154:            Enable or disable windows feature  
155:         
156:       .NOTES  
157:            Additional information about the function.  
158:  #>  
159:         
160:       [CmdletBinding()]  
161:       param  
162:       (  
163:                 [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$Name,  
164:                 [Parameter(Mandatory = $true)][ValidateSet('enable', 'disable')][string]$State  
165:       )  
166:         
167:       $EXE = $env:windir + "\system32\dism.exe"  
168:       Write-Host $Name"....." -NoNewline  
169:       If ($State -eq "enable") {  
170:            $Parameters = "/online /enable-feature /norestart /featurename:" + $Name  
171:       } else {  
172:            $Parameters = "/online /disable-feature /norestart /featurename:" + $Name  
173:       }  
174:       $ErrCode = (Start-Process -FilePath $EXE -ArgumentList $Parameters -Wait -PassThru -WindowStyle Minimized).ExitCode  
175:       If ($ErrCode -eq 0) {  
176:            $FeatureChange = Confirm-Feature -FeatureName $Name -FeatureState $State  
177:            If ($FeatureChange -eq $true) {  
178:                 If ($State -eq 'Enable') {  
179:                      Write-Host "Enabled" -ForegroundColor Yellow  
180:                 } else {  
181:                      Write-Host "Disabled" -ForegroundColor Yellow  
182:                 }  
183:            } else {  
184:                 Write-Host "Failed" -ForegroundColor Red  
185:            }  
186:       } elseif ($ErrCode -eq 3010) {  
187:            $FeatureChange = Confirm-Feature -FeatureName $Name -FeatureState $State  
188:            If ($FeatureChange -eq $true) {  
189:                 If ($State -eq 'Enable') {  
190:                      Write-Host "Enabled & Pending Reboot" -ForegroundColor Yellow  
191:                 } else {  
192:                      Write-Host "Disabled & Pending Reboot" -ForegroundColor Yellow  
193:                 }  
194:            } else {  
195:                 Write-Host "Failed" -ForegroundColor Red  
196:            }  
197:       } else {  
198:            If ($ErrCode -eq 50) {  
199:                 Write-Host "Failed. Parent feature needs to be enabled first." -ForegroundColor Red  
200:            } else {  
201:                 Write-Host "Failed with error code "$ErrCode -ForegroundColor Red  
202:            }  
203:       }  
204:  }  
205:    
206:  function Set-FeaturesFromFile {  
207:  <#  
208:       .SYNOPSIS  
209:            Set multiple features from a text file  
210:         
211:       .DESCRIPTION  
212:            This function reads the comma separated features and values from a text file and executes each feature.  
213:         
214:       .NOTES  
215:            Additional information about the function.  
216:  #>  
217:         
218:       [CmdletBinding()]  
219:       param ()  
220:         
221:       $RelativePath = (split-path $SCRIPT:MyInvocation.MyCommand.Path -parent) + '\'  
222:       $FeaturesFile = $RelativePath + $FeaturesFile  
223:       If ((Test-Path $FeaturesFile) -eq $true) {  
224:            $FeaturesFile = Get-Content $FeaturesFile  
225:            foreach ($Item in $FeaturesFile) {  
226:                 $Item = $Item.split(',')  
227:                 Set-WindowsFeature -Name $Item[0] -State $Item[1]  
228:            }  
229:       }  
230:  }  
231:    
232:  Clear-Host  
233:  If ($ListFeatures -eq $true) {  
234:       $WindowsFeatures = Get-WindowsFeaturesList  
235:       $WindowsFeatures  
236:  }  
237:  If ($FeaturesFile -ne '') {  
238:       Set-FeaturesFromFile  
239:  }  
240:  If ($Feature -ne '') {  
241:       Set-WindowsFeature -Name $Feature -State $Setting  
242:  }  
243:    

0 comments:

Post a Comment