From 6c0e58362fe9a670d010c2bbeb1983486867e4ec Mon Sep 17 00:00:00 2001 From: Heiko Stehli <43820014+stehlih@users.noreply.github.com> Date: Sun, 17 Jul 2022 19:33:52 +0200 Subject: [PATCH] VMNetworkAdapter: fix some issues on NetworkSetting parameter (#205) --- CHANGELOG.md | 4 + .../DSC_VMNetworkAdapter.psm1 | 119 ++++++++++-------- .../DSC_VMNetworkAdapter.schema.mof | 13 +- .../5-VMStaticNetworkSettings.ps1 | 4 +- tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 | 19 ++- 5 files changed, 86 insertions(+), 73 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6b2e7e..3285d6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,10 @@ For older change log history see the [historic changelog](HISTORIC_CHANGELOG.md) - Moved documentation to the HyperVDsc GitHub Wiki. - Updated all examples to correct folders and naming so they show up in the GitHub Wiki documentation and conceptual help. + - VMNetworkAdapter + - BREAKING CHANGE: Rename embedded instance class #203 + - Fix multiple DNS IP adresses does not work #190 + - NetworkSetting parameter is now optional and no default actions are taken if not specified ## [3.18.0] - 2022-06-04 diff --git a/source/DSCResources/DSC_VMNetworkAdapter/DSC_VMNetworkAdapter.psm1 b/source/DSCResources/DSC_VMNetworkAdapter/DSC_VMNetworkAdapter.psm1 index 9ce1e29..be24a8f 100644 --- a/source/DSCResources/DSC_VMNetworkAdapter/DSC_VMNetworkAdapter.psm1 +++ b/source/DSCResources/DSC_VMNetworkAdapter/DSC_VMNetworkAdapter.psm1 @@ -20,9 +20,6 @@ $script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US' .PARAMETER VMName Specifies the name of the VM to which the network adapter will be connected. Specify VMName as ManagementOS if you wish to connect the adapter to host OS. - -.PARAMETER IpAddress - Specifies the IpAddress information for the network adapter. #> function Get-TargetResource { @@ -88,7 +85,7 @@ function Get-TargetResource $networkInfo = Get-NetworkInformation -VMName $VMName -Name $Name if ($networkInfo) { - $item = New-CimInstance -ClassName MSFT_NetworkSettings -Property $networkInfo -Namespace root/microsoft/windows/desiredstateconfiguration -ClientOnly + $item = New-CimInstance -ClassName VMNetworkAdapterNetworkSettings -Property $networkInfo -Namespace root/microsoft/windows/desiredstateconfiguration -ClientOnly $configuration.Add('NetworkSetting', $item) } @@ -131,8 +128,8 @@ function Get-TargetResource Specifies the MAC address for the network adapter. This is not applicable if VMName is set to ManagementOS. Use this parameter to specify a static MAC address. -.PARAMETER IpAddress - Specifies the IpAddress information for the network adapter. +.PARAMETER NetworkSetting + Specifies the DHCP or IpAddress & DNS sever information for the network adapter. .PARAMETER VlanId Specifies the Vlan Id for the network adapter. @@ -269,44 +266,45 @@ function Set-TargetResource if ($VmName -ne 'ManagementOS') { - $networkInfo = Get-NetworkInformation -VMName $VMName -Name $Name - if (-not $NetworkSetting) + if ($null -ne $NetworkSetting) { - if ($networkInfo) + [boolean]$dhcpEnabled = $NetworkSetting.CimInstanceProperties['DHCPEnabled'].Value + + if ($dhcpEnabled) { Write-Verbose -Message $script:localizedData.EnableDhcp Set-NetworkInformation -VMName $VMName -Name $Name -Dhcp } - } - else - { - $parameters = @{ } - if ($ipAddress = $NetworkSetting.CimInstanceProperties['IpAddress'].Value) + else { - if (-not $ipAddress) + $parameters = @{} + if ($ipAddress = $NetworkSetting.CimInstanceProperties['IpAddress'].Value) { - throw $script:localizedData.MissingIPAndSubnet + if (-not $ipAddress) + { + throw $script:localizedData.MissingIPAndSubnet + } + $parameters.Add('IPAddress', $ipAddress) } - $parameters.Add('IPAddress', $ipAddress) - } - if ($subnet = $NetworkSetting.CimInstanceProperties['Subnet'].Value) - { - if (-not $subnet) + if ($subnet = $NetworkSetting.CimInstanceProperties['Subnet'].Value) { - throw $script:localizedData.MissingIPAndSubnet + if (-not $subnet) + { + throw $script:localizedData.MissingIPAndSubnet + } + $parameters.Add('Subnet', $subnet) + } + if ($defaultGateway = $NetworkSetting.CimInstanceProperties['DefaultGateway'].Value) + { + $parameters.Add('DefaultGateway', $defaultGateway) + } + if ($dnsServer = $NetworkSetting.CimInstanceProperties['DnsServer'].Value) + { + $parameters.Add('DnsServer', $dnsServer) } - $parameters.Add('Subnet', $subnet) - } - if ($defaultGateway = $NetworkSetting.CimInstanceProperties['DefaultGateway'].Value) - { - $parameters.Add('DefaultGateway', $defaultGateway) - } - if ($dnsServer = $NetworkSetting.CimInstanceProperties['DnsServer'].Value) - { - $parameters.Add('DnsServer', $dnsServer) - } - Set-NetworkInformation -VMName $VMName -Name $Name @parameters + Set-NetworkInformation -VMName $VMName -Name $Name @parameters + } } Write-Verbose -Message $script:localizedData.GetVMNetAdapterVlan @@ -363,8 +361,8 @@ function Set-TargetResource Specifies the MAC address for the network adapter. This is not applicable if VMName is set to ManagementOS. Use this parameter to specify a static MAC address. -.PARAMETER IpAddress - Specifies the IpAddress information for the network adapter. +.PARAMETER NetworkSetting + Specifies the DHCP or IpAddress & DNS sever information for the network adapter. .PARAMETER VlanId Specifies the Vlan Id for the network adapter. @@ -457,24 +455,28 @@ function Test-TargetResource } } - $networkInfo = Get-NetworkInformation -VMName $VMName -Name $Name - if (-not $NetworkSetting) + if ($null -ne $NetworkSetting) { - if ($networkInfo) - { - Write-Verbose -Message $script:localizedData.NotDhcp - return $false - } - } - else - { - if (-not $networkInfo) + $networkInfo = Get-NetworkInformation -VMName $VMName -Name $Name + + [boolean]$dhcpEnabled = $NetworkSetting.CimInstanceProperties['DHCPEnabled'].Value + + if ($dhcpEnabled) { - Write-Verbose -Message $script:localizedData.Dhcp - return $false + if (-not $networkInfo.DHCPEnabled) + { + Write-Verbose -Message $script:localizedData.NotDhcp + return $false + } } else { + if ($networkInfo.DHCPEnabled) + { + Write-Verbose -Message $script:localizedData.Dhcp + return $false + } + $ipAddress = $NetworkSetting.CimInstanceProperties['IpAddress'].Value $subnet = $NetworkSetting.CimInstanceProperties['Subnet'].Value $defaultGateway = $NetworkSetting.CimInstanceProperties['DefaultGateway'].Value @@ -497,10 +499,15 @@ function Test-TargetResource return $false } - if ($dnsServer -and -not $networkInfo.DNSServer.Split(',').Contains($dnsServer)) + if ($dnsServer ) { - Write-Verbose -Message $script:localizedData.DNSServerNotConfigured - return $false + $missingDns = $dnsServer | Where-Object {$networkInfo.DNSServer -notcontains $_} + + if ($missingDns.Count -gt 0) + { + Write-Verbose -Message $script:localizedData.DNSServerNotConfigured + return $false + } } } } @@ -601,18 +608,20 @@ function Get-NetworkInformation if ($networkSettings.DHCPEnabled) { - return $null + return @{ + DHCPEnabled = $true + } } else { return @{ + DHCPEnabled = $false IpAddress = $networkSettings.IPAddresses -join ',' Subnet = $networkSettings.Subnets -join ',' DefaultGateway = $networkSettings.DefaultGateways -join ',' - DnsServer = $networkSettings.DNSServers -join ',' + DnsServer = $networkSettings.DNSServers } } - } function Set-NetworkInformation @@ -645,7 +654,7 @@ function Set-NetworkInformation $DefaultGateway, [Parameter(ParameterSetName = 'Static')] - [System.String] + [System.String[]] $DnsServer ) diff --git a/source/DSCResources/DSC_VMNetworkAdapter/DSC_VMNetworkAdapter.schema.mof b/source/DSCResources/DSC_VMNetworkAdapter/DSC_VMNetworkAdapter.schema.mof index fcc010c..65b1930 100644 --- a/source/DSCResources/DSC_VMNetworkAdapter/DSC_VMNetworkAdapter.schema.mof +++ b/source/DSCResources/DSC_VMNetworkAdapter/DSC_VMNetworkAdapter.schema.mof @@ -1,10 +1,11 @@ [ClassVersion("2.0.0.0")] -Class NetworkSettings +Class VMNetworkAdapterNetworkSettings { - [Write, Description("IpAddress to give the network adapter. Only used if not Dhcp. Required if not Dhcp.")] string IpAddress; - [Write, Description("Subnet to give the network adapter. Only used if not Dhcp. Required if not Dhcp.")] string Subnet; - [Write, Description("DefaultGateway to give the network adapter. Only used if not Dhcp.")] string DefaultGateway; - [Write, Description("DNS server to give the network adapter. Only used if not Dhcp.")] string DnsServer; + [Write, Description("Specifies whether DHCP is enabled on the network adapter within the guest operating system.")] boolean DHCPEnabled; + [Write, Description("IPAddress configured on the network adapter within the guest operating system.")] string IPAddress; + [Write, Description("Subnet configured on the network adapter within the guest operating system.")] string Subnet; + [Write, Description("Default IP gateway configured on the network adapter within the guest operating system.")] string DefaultGateway; + [Write, Description("One or more DNS servers configured on the network adapter within the guest operating system.")] string DnsServer[]; }; [ClassVersion("2.0.0.0"), FriendlyName("VMNetworkAdapter")] @@ -15,7 +16,7 @@ class DSC_VMNetworkAdapter : OMI_BaseResource [Required, Description("Virtual Switch name to connect to.")] String SwitchName; [Required, Description("Name of the VM to attach to. If you want to attach new VM Network adapter to the management OS, set this property to 'ManagementOS'.")] String VMName; [Write, Description("Use this to specify a Static MAC Address. If this parameter is not specified, dynamic MAC Address will be set.")] String MacAddress; - [Write, Description("Network Settings of the network adapter. If this parameter is not supplied, DHCP will be used."), EmbeddedInstance("NetworkSettings")] String NetworkSetting; + [Write, Description("Network Settings of the network adapter. If this parameter is not supplied, DHCP will be used."), EmbeddedInstance("VMNetworkAdapterNetworkSettings")] String NetworkSetting; [Write, Description("Use this to specify a Vlan id on the Network Adapter.")] String VlanId; [Write, Description("Ensures that the VM Network Adapter is Present or Absent. The default value is `Present`."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; [Read, Description("Returns `$true` if the network adapter uses a dynamic MAC address.")] Boolean DynamicMacAddress; diff --git a/source/Examples/Resources/VMNetworkAdapter/5-VMStaticNetworkSettings.ps1 b/source/Examples/Resources/VMNetworkAdapter/5-VMStaticNetworkSettings.ps1 index 6c42b20..c4f16a5 100644 --- a/source/Examples/Resources/VMNetworkAdapter/5-VMStaticNetworkSettings.ps1 +++ b/source/Examples/Resources/VMNetworkAdapter/5-VMStaticNetworkSettings.ps1 @@ -14,12 +14,12 @@ Configuration Example SwitchName = 'SETSwitch' MacAddress = '001523be0c00' VMName = 'MyVM01' - NetworkSetting = NetworkSettings + NetworkSetting = VMNetworkAdapterNetworkSettings { IpAddress = '192.168.0.100' Subnet = '255.255.255.255' DefaultGateway = '192.168.0.1' - DnsServer = '192.168.0.1' + DnsServer = @( '192.168.0.1', '192.168.0.2' ) } } } diff --git a/tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 b/tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 index c571a4a..e9fa23a 100644 --- a/tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 +++ b/tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 @@ -41,11 +41,12 @@ try } $propertiesStatic = @{ - IpAddress = "192.168.0.1" - Subnet = "255.255.255.0" + DHCPEnabled = $false + IpAddress = "192.168.0.1" + Subnet = "255.255.255.0" } - $networkSettingsStatic = New-CimInstance -ClassName NetworkSettings -Property $properties -Namespace root/microsoft/windows/desiredstateconfiguration -ClientOnly + $networkSettingsStatic = New-CimInstance -ClassName VMNetworkAdapterNetworkSettings -Property $propertiesStatic -Namespace root/microsoft/windows/desiredstateconfiguration -ClientOnly $TestAdapter = [PSObject]@{ Id = $MockHostAdapter.Id @@ -98,7 +99,7 @@ try IpAddress = '10.10.10.10' Subnet = '255.255.255.0' DefaultGateway = '10.10.10.1' - DnsServer = '10.10.10.1' + DnsServer = @( '10.10.10.1' ) } } @@ -132,7 +133,7 @@ try IpAddress = '10.10.10.10' Subnet = '255.255.255.0' DefaultGateway = '10.10.10.1' - DnsServer = '10.10.10.1' + DnsServer = @( '10.10.10.1', '10.10.10.2' ) } } @@ -170,7 +171,6 @@ try } Mock -CommandName Remove-VMNetworkAdapter Mock -CommandName Set-VMNetworkAdapterVlan - Mock -CommandName Get-NetworkInformation Mock -CommandName Set-NetworkInformation It 'should not throw error' { @@ -184,7 +184,6 @@ try Assert-MockCalled -CommandName Set-VMNetworkAdapterVlan -Exactly 0 Assert-MockCalled -commandName Add-VMNetworkAdapter -Exactly 1 Assert-MockCalled -commandName Remove-VMNetworkAdapter -Exactly 0 - Assert-MockCalled -CommandName Get-NetworkInformation -Exactly 1 Assert-MockCalled -CommandName Set-NetworkInformation -Exactly 1 } } @@ -324,11 +323,11 @@ try } } - Context 'Adapter exists but network settings are not correct' { + Context 'Adapter exists and network settings are not specified' { Mock -CommandName Get-VMNetworkAdapter -MockWith { $MockAdapter } Mock -CommandName Get-VMNetworkAdapterVlan -MockWith { $MockAdapterVlanTagged } Mock -CommandName Get-NetworkInformation -MockWith { - @{ Dhcp = $false } + @{ DHCPEnabled = $false } } It 'should return false' { @@ -339,7 +338,7 @@ try } It 'should call expected Mocks' { Assert-MockCalled -commandName Get-VMNetworkAdapter -Exactly 1 - Assert-MockCalled -commandName Get-NetworkInformation -Exactly 1 + Assert-MockCalled -commandName Get-NetworkInformation -Exactly 0 } } }