mirror of
https://github.com/PowerShell/PSDscResources
synced 2026-06-21 13:45:29 +00:00
569 lines
18 KiB
PowerShell
569 lines
18 KiB
PowerShell
# Global needed to indicate if a restart is required
|
|
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '')]
|
|
param ()
|
|
|
|
$errorActionPreference = 'Stop'
|
|
Set-StrictMode -Version 'Latest'
|
|
|
|
Import-Module -Name (Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) `
|
|
-ChildPath 'CommonResourceHelper.psm1')
|
|
|
|
# Localized messages for verbose and error messages in this resource
|
|
$script:localizedData = Get-LocalizedData -ResourceName 'MSFT_WindowsFeature'
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Retrieves the status of the role or feature with the given name on the target machine.
|
|
|
|
.PARAMETER Name
|
|
The name of the role or feature to retrieve
|
|
|
|
.PARAMETER Credential
|
|
The credential (if required) to retrieve the role or feature.
|
|
Optional.
|
|
|
|
.NOTES
|
|
If the specified role or feature does not contain any subfeatures then
|
|
IncludeAllSubFeature will be set to $false. If the specified feature contains one
|
|
or more subfeatures then IncludeAllSubFeature will be set to $true only if all the
|
|
subfeatures are installed. Otherwise, IncludeAllSubFeature will be set to $false.
|
|
#>
|
|
function Get-TargetResource
|
|
{
|
|
[CmdletBinding()]
|
|
[OutputType([System.Collections.Hashtable])]
|
|
param
|
|
(
|
|
[Parameter(Mandatory = $true)]
|
|
[ValidateNotNullOrEmpty()]
|
|
[System.String]
|
|
$Name,
|
|
|
|
[Parameter()]
|
|
[ValidateNotNullOrEmpty()]
|
|
[System.Management.Automation.PSCredential]
|
|
[System.Management.Automation.Credential()]
|
|
$Credential
|
|
)
|
|
|
|
Write-Verbose -Message ($script:localizedData.GetTargetResourceStartMessage -f $Name)
|
|
|
|
Import-ServerManager
|
|
|
|
Write-Verbose -Message ($script:localizedData.QueryFeature -f $Name)
|
|
|
|
$isWinServer2008R2SP1 = Test-IsWinServer2008R2SP1
|
|
if ($isWinServer2008R2SP1 -and $PSBoundParameters.ContainsKey('Credential'))
|
|
{
|
|
$feature = Invoke-Command -ScriptBlock { Get-WindowsFeature -Name $Name } `
|
|
-ComputerName . `
|
|
-Credential $Credential `
|
|
}
|
|
else
|
|
{
|
|
$feature = Get-WindowsFeature @PSBoundParameters
|
|
}
|
|
|
|
Assert-SingleInstanceOfFeature -Feature $feature -Name $Name
|
|
|
|
$includeAllSubFeature = $true
|
|
|
|
if ($feature.SubFeatures.Count -eq 0)
|
|
{
|
|
$includeAllSubFeature = $false
|
|
}
|
|
else
|
|
{
|
|
foreach ($currentSubFeatureName in $feature.SubFeatures)
|
|
{
|
|
$getWindowsFeatureParameters = @{
|
|
Name = $currentSubFeatureName
|
|
}
|
|
|
|
if ($PSBoundParameters.ContainsKey('Credential'))
|
|
{
|
|
$getWindowsFeatureParameters['Credential'] = $Credential
|
|
}
|
|
|
|
if ($isWinServer2008R2SP1 -and $PSBoundParameters.ContainsKey('Credential'))
|
|
{
|
|
<#
|
|
Calling Get-WindowsFeature through Invoke-Command to start a new process with
|
|
the given credential since Get-WindowsFeature doesn't support the Credential
|
|
attribute on this server.
|
|
#>
|
|
$subFeature = Invoke-Command -ScriptBlock { Get-WindowsFeature -Name $currentSubFeatureName } `
|
|
-ComputerName . `
|
|
-Credential $Credential `
|
|
}
|
|
else
|
|
{
|
|
$subFeature = Get-WindowsFeature @getWindowsFeatureParameters
|
|
}
|
|
|
|
Assert-SingleInstanceOfFeature -Feature $subFeature -Name $currentSubFeatureName
|
|
|
|
if (-not $subFeature.Installed)
|
|
{
|
|
$includeAllSubFeature = $false
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($feature.Installed)
|
|
{
|
|
$ensureResult = 'Present'
|
|
}
|
|
else
|
|
{
|
|
$ensureResult = 'Absent'
|
|
}
|
|
|
|
Write-Verbose -Message ($script:localizedData.GetTargetResourceEndMessage -f $Name)
|
|
|
|
# Add all feature properties to the hash table
|
|
return @{
|
|
Name = $Name
|
|
DisplayName = $feature.DisplayName
|
|
Ensure = $ensureResult
|
|
IncludeAllSubFeature = $includeAllSubFeature
|
|
}
|
|
}
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Installs or uninstalls the role or feature with the given name on the target machine
|
|
with the option of installing or uninstalling all subfeatures as well.
|
|
|
|
.PARAMETER Name
|
|
The name of the role or feature to install or uninstall.
|
|
|
|
.PARAMETER Ensure
|
|
Specifies whether the role or feature should be installed ('Present')
|
|
or uninstalled ('Absent').
|
|
By default this is set to Present.
|
|
|
|
.PARAMETER IncludeAllSubFeature
|
|
Specifies whether or not all subfeatures should be installed or uninstalled with
|
|
the specified role or feature. Default is false.
|
|
If this property is true and Ensure is set to Present, all subfeatures will be installed.
|
|
If this property is false and Ensure is set to Present, subfeatures will not be installed or uninstalled.
|
|
If Ensure is set to Absent, all subfeatures will be uninstalled.
|
|
|
|
.PARAMETER Credential
|
|
The credential (if required) to install or uninstall the role or feature.
|
|
Optional.
|
|
|
|
.PARAMETER LogPath
|
|
The custom path to the log file to log this operation.
|
|
If not passed in, the default log path will be used (%windir%\logs\ServerManager.log).
|
|
#>
|
|
function Set-TargetResource
|
|
{
|
|
[CmdletBinding()]
|
|
param
|
|
(
|
|
[Parameter(Mandatory = $true)]
|
|
[ValidateNotNullOrEmpty()]
|
|
[System.String]
|
|
$Name,
|
|
|
|
[Parameter()]
|
|
[ValidateSet('Present', 'Absent')]
|
|
[System.String]
|
|
$Ensure = 'Present',
|
|
|
|
[Parameter()]
|
|
[System.Boolean]
|
|
$IncludeAllSubFeature = $false,
|
|
|
|
[Parameter()]
|
|
[ValidateNotNullOrEmpty()]
|
|
[System.Management.Automation.PSCredential]
|
|
[System.Management.Automation.Credential()]
|
|
$Credential,
|
|
|
|
[Parameter()]
|
|
[ValidateNotNullOrEmpty()]
|
|
[System.String]
|
|
$LogPath
|
|
)
|
|
|
|
Write-Verbose -Message ($script:localizedData.SetTargetResourceStartMessage -f $Name)
|
|
|
|
Import-ServerManager
|
|
|
|
$isWinServer2008R2SP1 = Test-IsWinServer2008R2SP1
|
|
|
|
if ($Ensure -eq 'Present')
|
|
{
|
|
$addWindowsFeatureParameters = @{
|
|
Name = $Name
|
|
IncludeAllSubFeature = $IncludeAllSubFeature
|
|
}
|
|
|
|
if ($PSBoundParameters.ContainsKey('LogPath'))
|
|
{
|
|
$addWindowsFeatureParameters['LogPath'] = $LogPath
|
|
}
|
|
|
|
Write-Verbose -Message ($script:localizedData.InstallFeature -f $Name)
|
|
|
|
if ($isWinServer2008R2SP1 -and $PSBoundParameters.ContainsKey('Credential'))
|
|
{
|
|
<#
|
|
Calling Add-WindowsFeature through Invoke-Command to start a new process with
|
|
the given credential since Add-WindowsFeature doesn't support the Credential
|
|
attribute on this server.
|
|
#>
|
|
$feature = Invoke-Command -ScriptBlock { Add-WindowsFeature @addWindowsFeatureParameters } `
|
|
-ComputerName . `
|
|
-Credential $Credential
|
|
}
|
|
else
|
|
{
|
|
if ($PSBoundParameters.ContainsKey('Credential'))
|
|
{
|
|
$addWindowsFeatureParameters['Credential'] = $Credential
|
|
}
|
|
|
|
$feature = Add-WindowsFeature @addWindowsFeatureParameters
|
|
}
|
|
|
|
if ($null -ne $feature -and $feature.Success)
|
|
{
|
|
Write-Verbose -Message ($script:localizedData.InstallSuccess -f $Name)
|
|
|
|
# Check if reboot is required, if so notify the Local Configuration Manager.
|
|
if ($feature.RestartNeeded -eq 'Yes')
|
|
{
|
|
Write-Verbose -Message $script:localizedData.RestartNeeded
|
|
$global:DSCMachineStatus = 1
|
|
}
|
|
}
|
|
else
|
|
{
|
|
New-InvalidOperationException -Message ($script:localizedData.FeatureInstallationFailureError -f $Name)
|
|
}
|
|
}
|
|
# Ensure = 'Absent'
|
|
else
|
|
{
|
|
$removeWindowsFeatureParameters = @{
|
|
Name = $Name
|
|
}
|
|
|
|
if ($PSBoundParameters.ContainsKey('LogPath'))
|
|
{
|
|
$removeWindowsFeatureParameters['LogPath'] = $LogPath
|
|
}
|
|
|
|
Write-Verbose -Message ($script:localizedData.UninstallFeature -f $Name)
|
|
|
|
if ($isWinServer2008R2SP1 -and $PSBoundParameters.ContainsKey('Credential'))
|
|
{
|
|
<#
|
|
Calling Remove-WindowsFeature through Invoke-Command to start a new process with
|
|
the given credential since Remove-WindowsFeature doesn't support the Credential
|
|
attribute on this server.
|
|
#>
|
|
$feature = Invoke-Command -ScriptBlock { Remove-WindowsFeature @removeWindowsFeatureParameters } `
|
|
-ComputerName . `
|
|
-Credential $Credential
|
|
}
|
|
else
|
|
{
|
|
if ($PSBoundParameters.ContainsKey('Credential'))
|
|
{
|
|
$addWindowsFeatureParameters['Credential'] = $Credential
|
|
}
|
|
|
|
$feature = Remove-WindowsFeature @removeWindowsFeatureParameters
|
|
}
|
|
|
|
if ($null -ne $feature -and $feature.Success)
|
|
{
|
|
Write-Verbose ($script:localizedData.UninstallSuccess -f $Name)
|
|
|
|
# Check if reboot is required, if so notify the Local Configuration Manager.
|
|
if ($feature.RestartNeeded -eq 'Yes')
|
|
{
|
|
Write-Verbose -Message $script:localizedData.RestartNeeded
|
|
$global:DSCMachineStatus = 1
|
|
}
|
|
}
|
|
else
|
|
{
|
|
New-InvalidOperationException -Message ($script:localizedData.FeatureUninstallationFailureError -f $Name)
|
|
}
|
|
}
|
|
|
|
Write-Verbose -Message ($script:localizedData.SetTargetResourceEndMessage -f $Name)
|
|
}
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Tests if the role or feature with the given name is in the desired state.
|
|
|
|
.PARAMETER Name
|
|
The name of the role or feature to test the state of.
|
|
|
|
.PARAMETER Ensure
|
|
Specifies whether the role or feature should be installed ('Present')
|
|
or uninstalled ('Absent').
|
|
By default this is set to Present.
|
|
|
|
.PARAMETER IncludeAllSubFeature
|
|
Specifies whether or not the installation state of all subfeatures should be tested with
|
|
the specified role or feature. Default is false.
|
|
-If this property is set to true and Ensure is set to Present,
|
|
each subfeature will be tested to ensure it is installed.
|
|
-If this property is set to true and Ensure is set to Absent,
|
|
each subfeature will be tested to ensure it is uninstalled.
|
|
-If this property is false, subfeatures will not be tested.
|
|
|
|
.PARAMETER Credential
|
|
The Credential (if required) to test the status of the role or feature.
|
|
Optional.
|
|
|
|
.PARAMETER LogPath
|
|
The path to the log file to log this operation.
|
|
Not used in Test-TargetResource.
|
|
|
|
#>
|
|
function Test-TargetResource
|
|
{
|
|
[CmdletBinding()]
|
|
[OutputType([System.Boolean])]
|
|
param
|
|
(
|
|
[Parameter(Mandatory = $true)]
|
|
[ValidateNotNullOrEmpty()]
|
|
[System.String]
|
|
$Name,
|
|
|
|
[Parameter()]
|
|
[ValidateSet('Present', 'Absent')]
|
|
[System.String]
|
|
$Ensure = 'Present',
|
|
|
|
[Parameter()]
|
|
[System.Boolean]
|
|
$IncludeAllSubFeature = $false,
|
|
|
|
[Parameter()]
|
|
[ValidateNotNullOrEmpty()]
|
|
[System.Management.Automation.PSCredential]
|
|
[System.Management.Automation.Credential()]
|
|
$Credential,
|
|
|
|
[Parameter()]
|
|
[ValidateNotNullOrEmpty()]
|
|
[System.String]
|
|
$LogPath
|
|
|
|
)
|
|
|
|
Write-Verbose -Message ($script:localizedData.TestTargetResourceStartMessage -f $Name)
|
|
|
|
Import-ServerManager
|
|
|
|
$testTargetResourceResult = $false
|
|
|
|
$getWindowsFeatureParameters = @{
|
|
Name = $Name
|
|
}
|
|
|
|
if ($PSBoundParameters.ContainsKey('Credential'))
|
|
{
|
|
$getWindowsFeatureParameters['Credential'] = $Credential
|
|
}
|
|
|
|
Write-Verbose -Message ($script:localizedData.QueryFeature -f $Name)
|
|
|
|
$isWinServer2008R2SP1 = Test-IsWinServer2008R2SP1
|
|
if ($isWinServer2008R2SP1 -and $PSBoundParameters.ContainsKey('Credential'))
|
|
{
|
|
<#
|
|
Calling Get-WindowsFeature through Invoke-Command to start a new process with
|
|
the given credential since Get-WindowsFeature doesn't support the Credential
|
|
attribute on this server.
|
|
#>
|
|
$feature = Invoke-Command -ScriptBlock { Get-WindowsFeature -Name $Name } `
|
|
-ComputerName . `
|
|
-Credential $Credential
|
|
}
|
|
else
|
|
{
|
|
$feature = Get-WindowsFeature @getWindowsFeatureParameters
|
|
}
|
|
|
|
Assert-SingleInstanceOfFeature -Feature $feature -Name $Name
|
|
|
|
# Check if the feature is in the requested Ensure state.
|
|
if (($Ensure -eq 'Present' -and $feature.Installed -eq $true) -or `
|
|
($Ensure -eq 'Absent' -and $feature.Installed -eq $false))
|
|
{
|
|
$testTargetResourceResult = $true
|
|
|
|
if ($IncludeAllSubFeature)
|
|
{
|
|
# Check if each subfeature is in the requested state.
|
|
foreach ($currentSubFeatureName in $feature.SubFeatures)
|
|
{
|
|
$getWindowsFeatureParameters['Name'] = $currentSubFeatureName
|
|
|
|
if ($isWinServer2008R2SP1 -and $PSBoundParameters.ContainsKey('Credential'))
|
|
{
|
|
<#
|
|
Calling Get-WindowsFeature through Invoke-Command to start a new process with
|
|
the given credential since Get-WindowsFeature doesn't support the Credential
|
|
attribute on this server.
|
|
#>
|
|
$subFeature = Invoke-Command -ScriptBlock { Get-WindowsFeature -Name $currentSubFeatureName } `
|
|
-ComputerName . `
|
|
-Credential $Credential
|
|
}
|
|
else
|
|
{
|
|
$subFeature = Get-WindowsFeature @getWindowsFeatureParameters
|
|
}
|
|
|
|
Assert-SingleInstanceOfFeature -Feature $subFeature -Name $currentSubFeatureName
|
|
|
|
if (-not $subFeature.Installed -and $Ensure -eq 'Present')
|
|
{
|
|
$testTargetResourceResult = $false
|
|
break
|
|
}
|
|
|
|
if ($subFeature.Installed -and $Ensure -eq 'Absent')
|
|
{
|
|
$testTargetResourceResult = $false
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
# Ensure is not in the correct state
|
|
$testTargetResourceResult = $false
|
|
}
|
|
|
|
Write-Verbose -Message ($script:localizedData.TestTargetResourceEndMessage -f $Name)
|
|
|
|
return $testTargetResourceResult
|
|
}
|
|
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Asserts that a single instance of the given role or feature exists.
|
|
|
|
.PARAMETER Feature
|
|
The role or feature object to check.
|
|
|
|
.PARAMETER Name
|
|
The name of the role or feature to include in any error messages that are thrown.
|
|
(Not used to assert validity of the feature).
|
|
#>
|
|
function Assert-SingleInstanceOfFeature
|
|
{
|
|
[CmdletBinding()]
|
|
param
|
|
(
|
|
[Parameter()]
|
|
[System.Management.Automation.PSObject]
|
|
$Feature,
|
|
|
|
[Parameter()]
|
|
[System.String]
|
|
$Name
|
|
)
|
|
|
|
if ($null -eq $Feature)
|
|
{
|
|
New-InvalidOperationException -Message ($script:localizedData.FeatureNotFoundError -f $Name)
|
|
}
|
|
|
|
if ($Feature -is [Array] -and $Feature.Count -gt 1)
|
|
{
|
|
New-InvalidOperationException -Message ($script:localizedData.MultipleFeatureInstancesError -f $Name)
|
|
}
|
|
}
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Sets up the ServerManager module on the target node.
|
|
Throws an error if not on a machine running Windows Server.
|
|
#>
|
|
function Import-ServerManager
|
|
{
|
|
param
|
|
()
|
|
|
|
<#
|
|
Enable ServerManager-PSH-Cmdlets feature if OS is WS2008R2 Core.
|
|
Datacenter = 12, Standard = 13, Enterprise = 14
|
|
#>
|
|
$serverCoreOSCodes = @( 12, 13, 14 )
|
|
|
|
$operatingSystem = Get-CimInstance -Class 'Win32_OperatingSystem'
|
|
|
|
# Check if this operating system needs an update to the ServerManager cmdlets
|
|
if ($operatingSystem.Version.StartsWith('6.1.') -and `
|
|
$serverCoreOSCodes -contains $operatingSystem.OperatingSystemSKU)
|
|
{
|
|
Write-Verbose -Message $script:localizedData.EnableServerManagerPSHCmdletsFeature
|
|
|
|
<#
|
|
ServerManager-PSH-Cmdlets has a depndency on Powershell 2 update: MicrosoftWindowsPowerShell,
|
|
so enabling the MicrosoftWindowsPowerShell update.
|
|
#>
|
|
$null = Dism\online\enable-feature\FeatureName:MicrosoftWindowsPowerShell
|
|
$null = Dism\online\enable-feature\FeatureName:ServerManager-PSH-Cmdlets
|
|
}
|
|
|
|
try
|
|
{
|
|
Import-Module -Name 'ServerManager' -ErrorAction Stop
|
|
}
|
|
catch [System.Management.Automation.RuntimeException]
|
|
{
|
|
if ($_.Exception.Message -like "*Some or all identity references could not be translated*")
|
|
{
|
|
Write-Verbose -Message $script:localizedData.IdentityNotFoundMessage
|
|
}
|
|
else
|
|
{
|
|
Write-Verbose -Message $script:localizedData.ServerManagerModuleNotFoundMessage
|
|
New-InvalidOperationException -Message $script:localizedData.SkuNotSupported
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
Write-Verbose -Message $script:localizedData.ServerManagerModuleNotFoundMessage
|
|
New-InvalidOperationException -Message $script:localizedData.SkuNotSupported
|
|
}
|
|
}
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Tests if the machine is a Windows Server 2008 R2 SP1 machine.
|
|
|
|
.NOTES
|
|
Since Assert-PrequisitesValid ensures that ServerManager is available on the machine,
|
|
this function only checks the OS version.
|
|
#>
|
|
function Test-IsWinServer2008R2SP1
|
|
{
|
|
param
|
|
()
|
|
|
|
return ([Environment]::OSVersion.Version.ToString().Contains('6.1.'))
|
|
}
|
|
|
|
Export-ModuleMember -Function *-TargetResource
|