DSCResources/MSFT_xVMSwitch/MSFT_xVMSwitch.psm1
$script:dscResourceCommonModulePath = Join-Path -Path $PSScriptRoot -ChildPath '../../Modules/DscResource.Common' $script:hyperVDscCommonModulePath = Join-Path -Path $PSScriptRoot -ChildPath '../../Modules/HyperVDsc.Common' Import-Module -Name $script:dscResourceCommonModulePath Import-Module -Name $script:hyperVDscCommonModulePath $script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US' <# .SYNOPSIS Gets MSFT_xVMSwitch resource current state. .PARAMETER Name Name of the VM Switch. .PARAMETER Type Type of switch. #> function Get-TargetResource { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( [Parameter(Mandatory = $true)] [System.String] $Name, [Parameter(Mandatory = $true)] [ValidateSet('External', 'Internal', 'Private')] [System.String] $Type ) Write-Verbose -Message "Getting settings for VM Switch '$Name'" # Check if Hyper-V module is present for Hyper-V cmdlets if (!(Get-Module -ListAvailable -Name Hyper-V)) { $errorMessage = $script:localizedData.HyperVNotInstalledError New-ObjectNotFoundException -Message $errorMessage } $switch = Get-VMSwitch -Name $Name -SwitchType $Type -ErrorAction SilentlyContinue if ($null -ne $switch) { $ensure = 'Present' if ($switch.SwitchType -eq 'External') { if ($switch.EmbeddedTeamingEnabled -ne $true) { $netAdapterName = (Get-NetAdapter -InterfaceDescription $switch.NetAdapterInterfaceDescription -ErrorAction SilentlyContinue).Name $description = $switch.NetAdapterInterfaceDescription $loadBalancingAlgorithm = 'NA' } else { $netAdapterName = (Get-NetAdapter -InterfaceDescription $switch.NetAdapterInterfaceDescriptions).Name $description = $switch.NetAdapterInterfaceDescriptions $loadBalancingAlgorithm = ($switch | Get-VMSwitchTeam).LoadBalancingAlgorithm.toString() } } else { $netAdapterName = $null $description = $null } } else { $ensure = 'Absent' } $returnValue = @{ Name = $switch.Name Type = $switch.SwitchType NetAdapterName = [System.String[]] $netAdapterName AllowManagementOS = $switch.AllowManagementOS EnableEmbeddedTeaming = $switch.EmbeddedTeamingEnabled LoadBalancingAlgorithm = $loadBalancingAlgorithm Ensure = $ensure Id = $switch.Id NetAdapterInterfaceDescription = $description } if ($null -ne $switch.BandwidthReservationMode) { $returnValue['BandwidthReservationMode'] = $switch.BandwidthReservationMode } else { $returnValue['BandwidthReservationMode'] = 'NA' } return $returnValue } <# .SYNOPSIS Configures MSFT_xVMSwitch resource state. .PARAMETER Name Name of the VM Switch. .PARAMETER Type Type of switch. .PARAMETER NetAdapterName Network adapter name(s) for external switch type. .PARAMETER AllowManagementOS Specify if the VM host has access to the physical NIC. .PARAMETER EnableEmbeddedTeaming Should embedded NIC teaming be used (Windows Server 2016 only). .PARAMETER BandwidthReservationMode Type of Bandwidth Reservation Mode to use for the switch. .PARAMETER LoadBalancingAlgorithm The load balancing algorithm that this switch team use. .PARAMETER Id Desired unique ID of the Hyper-V Switch (Windows Server 2016 only). .PARAMETER Ensure Whether switch should be present or absent. #> function Set-TargetResource { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [System.String] $Name, [Parameter(Mandatory = $true)] [ValidateSet('External', 'Internal', 'Private')] [System.String] $Type, [Parameter()] [ValidateNotNullOrEmpty()] [System.String[]] $NetAdapterName, [Parameter()] [System.Boolean] $AllowManagementOS = $false, [Parameter()] [System.Boolean] $EnableEmbeddedTeaming = $false, [Parameter()] [ValidateSet('Default', 'Weight', 'Absolute', 'None', 'NA')] [System.String] $BandwidthReservationMode = 'NA', [Parameter()] [ValidateSet('Dynamic', 'HyperVPort')] [System.String] $LoadBalancingAlgorithm, [Parameter()] [ValidateNotNullOrEmpty()] [ValidateScript( { $testGuid = New-Guid if ([guid]::TryParse($_, [ref]$testGuid)) { return $true } else { throw 'The VMSwitch Id must be in GUID format!' } } )] [System.String] $Id, [Parameter()] [ValidateSet('Present', 'Absent')] [System.String] $Ensure = 'Present' ) # Check if Hyper-V module is present for Hyper-V cmdlets if (!(Get-Module -ListAvailable -Name Hyper-V)) { $errorMessage = $script:localizedData.HyperVNotInstalledError New-ObjectNotFoundException -Message $errorMessage } # Check to see if the BandwidthReservationMode chosen is supported in the OS elseif (($BandwidthReservationMode -ne 'NA') -and ((Get-OSVersion) -lt [version]'6.2.0')) { $errorMessage = $script:localizedData.BandwidthReservationModeError New-InvalidOperationException -Message $errorMessage } if ($EnableEmbeddedTeaming -eq $true -and (Get-OSVersion).Major -lt 10) { $errorMessage = $script:localizedData.SETServer2016Error New-InvalidOperationException -Message $errorMessage } if (($PSBoundParameters.ContainsKey('Id')) -and (Get-OSVersion).Major -lt 10) { $errorMessage = $script:localizedData.VMSwitchIDServer2016Error New-InvalidOperationException -Message $errorMessage } if ($Ensure -eq 'Present') { $switch = (Get-VMSwitch -Name $Name -SwitchType $Type -ErrorAction SilentlyContinue) # If switch is present and it is external type, that means it doesn't have right properties (TEST code ensures that) if ($switch -and ($switch.SwitchType -eq 'External')) { $removeReaddSwitch = $false Write-Verbose -Message ($script:localizedData.CheckingSwitchMessage -f $Name) if ($switch.EmbeddedTeamingEnabled -eq $false -or $null -eq $switch.EmbeddedTeamingEnabled) { if ((Get-NetAdapter -Name $NetAdapterName).InterfaceDescription -ne $switch.NetAdapterInterfaceDescription) { Write-Verbose -Message ($script:localizedData.NetAdapterInterfaceIncorrectMessage -f $Name) $removeReaddSwitch = $true } } else { $adapters = (Get-NetAdapter -InterfaceDescription $switch.NetAdapterInterfaceDescriptions -ErrorAction SilentlyContinue).Name if ($null -ne (Compare-Object -ReferenceObject $adapters -DifferenceObject $NetAdapterName)) { Write-Verbose -Message ($script:localizedData.SwitchIncorrectNetworkAdapters -f $Name) $removeReaddSwitch = $true } } if (($BandwidthReservationMode -ne 'NA') -and ($switch.BandwidthReservationMode -ne $BandwidthReservationMode)) { Write-Verbose -Message ($script:localizedData.BandwidthReservationModeIncorrect -f $Name) $removeReaddSwitch = $true } if ($null -ne $switch.EmbeddedTeamingEnabled -and $switch.EmbeddedTeamingEnabled -ne $EnableEmbeddedTeaming) { Write-Verbose -Message ($script:localizedData.EnableEmbeddedTeamingIncorrect -f $Name) $removeReaddSwitch = $true } if ($null -ne $switch.EmbeddedTeamingEnabled -and $switch.EmbeddedTeamingEnabled -ne $EnableEmbeddedTeaming) { Write-Verbose -Message ($script:localizedData.EnableEmbeddedTeamingIncorrect -f $Name) $removeReaddSwitch = $true } if ($PSBoundParameters.ContainsKey('Id') -and $switch.Id -ne $Id) { Write-Verbose -Message ($script:localizedData.IdIncorrect -f $Name) $removeReaddSwitch = $true } if ($removeReaddSwitch) { Write-Verbose -Message ($script:localizedData.RemoveAndReaddSwitchMessage -f $Name) $switch | Remove-VMSwitch -Force $parameters = @{ } $parameters['Name'] = $Name $parameters['NetAdapterName'] = $NetAdapterName if ($BandwidthReservationMode -ne 'NA') { $parameters['MinimumBandwidthMode'] = $BandwidthReservationMode } if ($PSBoundParameters.ContainsKey('AllowManagementOS')) { $parameters['AllowManagementOS'] = $AllowManagementOS } if ($PSBoundParameters.ContainsKey('EnableEmbeddedTeaming')) { $parameters['EnableEmbeddedTeaming'] = $EnableEmbeddedTeaming } if ($PSBoundParameters.ContainsKey('Id')) { $parameters['Id'] = $Id.ToString() } $null = New-VMSwitch @parameters # Since the switch is recreated, the $switch variable is stale and needs to be reassigned $switch = (Get-VMSwitch -Name $Name -SwitchType $Type -ErrorAction SilentlyContinue) } else { Write-Verbose -Message ($script:localizedData.SwitchCorrectNetAdapterAndBandwidthMode -f $Name, ($NetAdapterName -join ','), $BandwidthReservationMode) } Write-Verbose -Message ($script:localizedData.CheckAllowManagementOS -f $Name) if ($PSBoundParameters.ContainsKey('AllowManagementOS') -and ($switch.AllowManagementOS -ne $AllowManagementOS)) { Write-Verbose -Message ($script:localizedData.AllowManagementOSIncorrect -f $Name) $switch | Set-VMSwitch -AllowManagementOS $AllowManagementOS Write-Verbose -Message ($script:localizedData.AllowManagementOSUpdated -f $Name, $AllowManagementOS) } else { Write-Verbose -Message ($script:localizedData.AllowManagementOSCorrect -f $Name) } } # If the switch is not present, create one else { Write-Verbose -Message ($script:localizedData.PresentNotCorrect -f $Name, $Ensure) Write-Verbose -Message $script:localizedData.CreatingSwitch $parameters = @{ } $parameters['Name'] = $Name if ($BandwidthReservationMode -ne 'NA') { $parameters['MinimumBandwidthMode'] = $BandwidthReservationMode } if ($NetAdapterName) { $parameters['NetAdapterName'] = $NetAdapterName if ($PSBoundParameters.ContainsKey('AllowManagementOS')) { $parameters['AllowManagementOS'] = $AllowManagementOS } } else { $parameters['SwitchType'] = $Type } if ($PSBoundParameters.ContainsKey('EnableEmbeddedTeaming')) { $parameters['EnableEmbeddedTeaming'] = $EnableEmbeddedTeaming } if ($PSBoundParameters.ContainsKey('Id')) { $parameters['Id'] = $Id } $switch = New-VMSwitch @parameters Write-Verbose -Message ($script:localizedData.PresentCorrect -f $Name, $Ensure) } # Set the load balancing algorithm if it's a SET Switch and the parameter is specified if ($EnableEmbeddedTeaming -eq $true -and $PSBoundParameters.ContainsKey('LoadBalancingAlgorithm')) { Write-Verbose -Message ($script:localizedData.SetLoadBalancingAlgorithmMessage -f $Name, $LoadBalancingAlgorithm) Set-VMSwitchTeam -Name $switch.Name -LoadBalancingAlgorithm $LoadBalancingAlgorithm -Verbose } } # Ensure is set to 'Absent', remove the switch else { Get-VMSwitch $Name -ErrorAction SilentlyContinue | Remove-VMSwitch -Force } } <# .SYNOPSIS Tests if MSFT_xVMSwitch resource state is in the desired state or not. .PARAMETER Name Name of the VM Switch. .PARAMETER Type Type of switch. .PARAMETER NetAdapterName Network adapter name(s) for external switch type. .PARAMETER AllowManagementOS Specify if the VM host has access to the physical NIC. .PARAMETER EnableEmbeddedTeaming Should embedded NIC teaming be used (Windows Server 2016 only). .PARAMETER BandwidthReservationMode Type of Bandwidth Reservation Mode to use for the switch. .PARAMETER LoadBalancingAlgorithm The load balancing algorithm that this switch team use. .PARAMETER Id Desired unique ID of the Hyper-V Switch (Windows Server 2016 only). .PARAMETER Ensure Whether switch should be present or absent. #> function Test-TargetResource { [CmdletBinding()] [OutputType([System.Boolean])] param ( [Parameter(Mandatory = $true)] [System.String] $Name, [Parameter(Mandatory = $true)] [ValidateSet('External', 'Internal', 'Private')] [System.String] $Type, [Parameter()] [ValidateNotNullOrEmpty()] [System.String[]] $NetAdapterName, [Parameter()] [System.Boolean] $AllowManagementOS = $false, [Parameter()] [System.Boolean] $EnableEmbeddedTeaming = $false, [Parameter()] [ValidateSet('Default', 'Weight', 'Absolute', 'None', 'NA')] [System.String] $BandwidthReservationMode = 'NA', [Parameter()] [ValidateSet('Dynamic', 'HyperVPort')] [System.String] $LoadBalancingAlgorithm, [Parameter()] [ValidateNotNullOrEmpty()] [ValidateScript( { $testGuid = New-Guid if ([guid]::TryParse($_, [ref]$testGuid)) { return $true } else { throw 'The VMSwitch Id must be in GUID format!' } } )] [System.String] $Id, [Parameter()] [ValidateSet('Present', 'Absent')] [System.String] $Ensure = 'Present' ) # Check if Hyper-V module is present for Hyper-V cmdlets if (!(Get-Module -ListAvailable -Name Hyper-V)) { $errorMessage = $script:localizedData.HyperVNotInstalledError New-ObjectNotFoundException -Message $errorMessage } #region input validation if ($Type -eq 'External' -and !($NetAdapterName)) { $errorMessage = $script:localizedData.NetAdapterNameRequiredError New-InvalidArgumentException -ArgumentName 'Type' -Message $errorMessage } if ($Type -ne 'External' -and $NetAdapterName) { $errorMessage = $script:localizedData.NetAdapterNameNotRequiredError New-InvalidArgumentException -ArgumentName 'Type' -Message $errorMessage } if (($BandwidthReservationMode -ne 'NA') -and ((Get-OSVersion) -lt [version]'6.2.0')) { $errorMessage = $script:localizedData.BandwidthReservationModeError New-InvalidArgumentException -ArgumentName 'BandwidthReservationMode' -Message $errorMessage } if ($EnableEmbeddedTeaming -eq $true -and (Get-OSVersion).Major -lt 10) { $errorMessage = $script:localizedData.SETServer2016Error New-InvalidArgumentException -ArgumentName 'EnableEmbeddedTeaming' -Message $errorMessage } if (($PSBoundParameters.ContainsKey('Id')) -and (Get-OSVersion).Major -lt 10) { $errorMessage = $script:localizedData.VMSwitchIDServer2016Error New-InvalidOperationException -Message $errorMessage } #endregion try { # Check if switch exists Write-Verbose -Message ($script:localizedData.PresentChecking -f $Name, $Ensure) $switch = Get-VMSwitch -Name $Name -SwitchType $Type -ErrorAction Stop # If switch exists if ($null -ne $switch) { Write-Verbose -Message ($script:localizedData.SwitchPresent -f $Name) # If switch should be present, check the switch type if ($Ensure -eq 'Present') { ## Only check the BandwidthReservationMode if specified if ($PSBoundParameters.ContainsKey('BandwidthReservationMode')) { # If the BandwidthReservationMode is correct, or if $switch.BandwidthReservationMode is $null which means it isn't supported on the OS Write-Verbose -Message ($script:localizedData.CheckingBandwidthReservationMode -f $Name) if ($switch.BandwidthReservationMode -eq $BandwidthReservationMode -or $null -eq $switch.BandwidthReservationMode) { Write-Verbose -Message ($script:localizedData.BandwidthReservationModeCorrect -f $Name) } else { Write-Verbose -Message ($script:localizedData.BandwidthReservationModeIncorrect -f $Name) return $false } } # If switch is the external type, check additional properties if ($Type -eq 'External') { if ($EnableEmbeddedTeaming -eq $false) { Write-Verbose -Message ($script:localizedData.CheckingNetAdapterInterface -f $Name) $adapter = $null try { $adapter = Get-NetAdapter -Name $NetAdapterName -ErrorAction SilentlyContinue } catch { # There are scenarios where the SilentlyContinue error action is not honoured, # so this block serves to handle those and the write-verbose message is here # to ensure that script analyser doesn't see an empty catch block to throw an # error Write-Verbose -Message $script:localizedData.NetAdapterNotFound } if ($adapter.InterfaceDescription -ne $switch.NetAdapterInterfaceDescription) { return $false } else { Write-Verbose -Message ($script:localizedData.NetAdapterInterfaceCorrect -f $Name) } } else { Write-Verbose -Message ($script:localizedData.CheckingNetAdapterInterfaces -f $Name) if ($null -ne $switch.NetAdapterInterfaceDescriptions) { $adapters = (Get-NetAdapter -InterfaceDescription $switch.NetAdapterInterfaceDescriptions -ErrorAction SilentlyContinue).Name if ($null -ne (Compare-Object -ReferenceObject $adapters -DifferenceObject $NetAdapterName)) { Write-Verbose -Message ($script:localizedData.IncorrectNetAdapterInterfaces -f $Name) return $false } else { Write-Verbose -Message ($script:localizedData.CorrectNetAdapterInterfaces -f $Name) } } else { Write-Verbose -Message ($script:localizedData.IncorrectNetAdapterInterfaces -f $Name) return $false } } if ($PSBoundParameters.ContainsKey('AllowManagementOS')) { Write-Verbose -Message ($script:localizedData.CheckAllowManagementOS -f $Name) if (($switch.AllowManagementOS -ne $AllowManagementOS)) { return $false } else { Write-Verbose -Message ($script:localizedData.AllowManagementOSCorrect -f $Name) } } if ($PSBoundParameters.ContainsKey('LoadBalancingAlgorithm')) { Write-Verbose -Message ($script:localizedData.CheckingLoadBalancingAlgorithm -f $Name) $loadBalancingAlgorithm = ($switch | Get-VMSwitchTeam).LoadBalancingAlgorithm.toString() if ($loadBalancingAlgorithm -ne $LoadBalancingAlgorithm) { return $false } else { Write-Verbose -Message ($script:localizedData.LoadBalancingAlgorithmCorrect -f $Name) } } } # Only check embedded teaming if specified if ($PSBoundParameters.ContainsKey('EnableEmbeddedTeaming') -eq $true) { Write-Verbose -Message ($script:localizedData.CheckEnableEmbeddedTeaming -f $Name) if ($switch.EmbeddedTeamingEnabled -eq $EnableEmbeddedTeaming -or $null -eq $switch.EmbeddedTeamingEnabled) { Write-Verbose -Message ($script:localizedData.EnableEmbeddedTeamingCorrect -f $Name) } else { Write-Verbose -Message ($script:localizedData.EnableEmbeddedTeamingIncorrect -f $Name) return $false } } # Check if the Switch has the desired ID if ($PSBoundParameters.ContainsKey('Id') -eq $true) { Write-Verbose -Message ($script:localizedData.CheckID -f $Name) if ($switch.Id.Guid -eq $Id) { Write-Verbose -Message ($script:localizedData.IdCorrect -f $Name) } else { Write-Verbose -Message ($script:localizedData.IdIncorrect -f $Name) return $false } } return $true } # If switch should be absent, but is there, return $false else { return $false } } else { if ($Ensure -eq 'Present') { return $false } else { return $true } } } # If no switch was present catch [System.Management.Automation.ActionPreferenceStopException] { Write-Verbose -Message ($script:localizedData.SwitchNotPresent -f $Name) return ($Ensure -eq 'Absent') } } <# .SYNOPSIS Returns the OS version #> function Get-OSVersion { [Environment]::OSVersion.Version } Export-ModuleMember -Function *-TargetResource |