From 103a087802fa288cd47eabe1ef1f45affeaeaabd Mon Sep 17 00:00:00 2001 From: Jan-Hendrik Peters Date: Tue, 17 Jan 2023 15:22:59 +0100 Subject: [PATCH 1/9] VMNetworkAdapter Device Naming VMNetworkAdapter MAC Spoofing --- CHANGELOG.md | 2 + .../DSC_VMNetworkAdapter.psm1 | 61 ++++++++++++++++++- .../DSC_VMNetworkAdapter.schema.mof | 2 + .../en-US/DSC_VMNetworkAdapter.strings.psd1 | 4 ++ tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 | 28 +++++---- 5 files changed, 85 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7184a62..8f21963 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ For older change log history see the [historic changelog](HISTORIC_CHANGELOG.md) - Fix multiple DNS IP adresses does not work #190 - NetworkSetting parameter is now optional and no default actions are taken if not specified - Switch to use VM image `windows-latest` to build phase. + - DeviceNaming parameter added #206 + - MacAddressSpoofing parameter added #206 ## [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 be24a8f..373c73b 100644 --- a/source/DSCResources/DSC_VMNetworkAdapter/DSC_VMNetworkAdapter.psm1 +++ b/source/DSCResources/DSC_VMNetworkAdapter/DSC_VMNetworkAdapter.psm1 @@ -134,6 +134,12 @@ function Get-TargetResource .PARAMETER VlanId Specifies the Vlan Id for the network adapter. +.PARAMETER DeviceNaming + Use this to enable or disable Device Naming. Default: Off + +.PARAMETER MacAddressSpoofing + Use this to enable or disable MAC address spoofing. Default: Off + .PARAMETER Ensure Specifies if the network adapter should be Present or Absent. #> @@ -170,6 +176,16 @@ function Set-TargetResource [System.String] $VlanId, + [Parameter()] + [ValidateSet('On', 'Off')] + [System.String] + $DeviceNaming = 'Off', + + [Parameter()] + [ValidateSet('On', 'Off')] + [System.String] + $MacAddressSpoofing = 'Off', + [Parameter()] [ValidateSet('Present', 'Absent')] [System.String] @@ -232,7 +248,10 @@ function Set-TargetResource { Write-Verbose -Message $script:localizedData.PerformVMNetModify - $setArguments = @{ } + $setArguments = @{ + DeviceNaming = $DeviceNaming + MacAddressSpoofing = $MacAddressSpoofing + } $setArguments.Add('VMNetworkAdapter', $netAdapterExists) if ($MacAddress) { @@ -259,9 +278,11 @@ function Set-TargetResource $arguments.Add('StaticMacAddress', $MacAddress) } $arguments.Add('SwitchName', $SwitchName) + $arguments.Add('DeviceNaming', $DeviceNaming) } Write-Verbose -Message $script:localizedData.AddVMNetAdapter $netAdapterExists = Add-VMNetworkAdapter @arguments -Passthru -ErrorAction Stop + Set-VMNetworkAdapter -VMNetworkAdapter $netAdapterExists -MacAddressSpoofing $MacAddressSpoofing } if ($VmName -ne 'ManagementOS') @@ -367,6 +388,12 @@ function Set-TargetResource .PARAMETER VlanId Specifies the Vlan Id for the network adapter. +.PARAMETER DeviceNaming + Use this to enable or disable Device Naming. Default: Off + +.PARAMETER MacAddressSpoofing + Use this to enable or disable MAC address spoofing. Default: Off + .PARAMETER Ensure Specifies if the network adapter should be Present or Absent. #> @@ -404,6 +431,16 @@ function Test-TargetResource [System.String] $VlanId, + [Parameter()] + [ValidateSet('On', 'Off')] + [System.String] + $DeviceNaming = 'Off', + + [Parameter()] + [ValidateSet('On', 'Off')] + [System.String] + $MacAddressSpoofing = 'Off', + [Parameter()] [ValidateSet('Present', 'Absent')] [System.String] @@ -558,6 +595,28 @@ function Test-TargetResource return $true } + if ($netAdapterExists.MacAddressSpoofing -ne $MacAddressSpoofing) + { + Write-Verbose -Message $script:localizedData.SpoofingDifferent + return $false + } + else + { + Write-Verbose -Message $script:localizedData.SpoofingConfiguredNoActionNeeded + return $true + + } + + if ($netAdapterExists.DeviceNaming -ne $DeviceNaming) + { + Write-Verbose -Message $script:localizedData.DeviceNamingDifferent + return $false + } + else + { + Write-Verbose -Message $script:localizedData.DeviceNamingConfiguredNoActionNeeded + return $true + } } else { diff --git a/source/DSCResources/DSC_VMNetworkAdapter/DSC_VMNetworkAdapter.schema.mof b/source/DSCResources/DSC_VMNetworkAdapter/DSC_VMNetworkAdapter.schema.mof index 65b1930..9cc512b 100644 --- a/source/DSCResources/DSC_VMNetworkAdapter/DSC_VMNetworkAdapter.schema.mof +++ b/source/DSCResources/DSC_VMNetworkAdapter/DSC_VMNetworkAdapter.schema.mof @@ -19,5 +19,7 @@ class DSC_VMNetworkAdapter : OMI_BaseResource [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; + [Write, Description("Use this to enable or disable Device Naming. Default: Off"), ValueMap{"On","Off"}, Values{"On","Off"}] String DeviceNaming; + [Write, Description("Use this to enable or disable MAC address spoofing. Default: Off"), ValueMap{"On","Off"}, Values{"On","Off"}] String MacAddressSpoofing; [Read, Description("Returns `$true` if the network adapter uses a dynamic MAC address.")] Boolean DynamicMacAddress; }; diff --git a/source/DSCResources/DSC_VMNetworkAdapter/en-US/DSC_VMNetworkAdapter.strings.psd1 b/source/DSCResources/DSC_VMNetworkAdapter/en-US/DSC_VMNetworkAdapter.strings.psd1 index d142c76..cdc9c77 100644 --- a/source/DSCResources/DSC_VMNetworkAdapter/en-US/DSC_VMNetworkAdapter.strings.psd1 +++ b/source/DSCResources/DSC_VMNetworkAdapter/en-US/DSC_VMNetworkAdapter.strings.psd1 @@ -27,4 +27,8 @@ ConvertFrom-StringData @' VMNetAdapterDoesNotExistNoActionNeeded=VM Network adapter does not exist. No action needed. SwitchIsDifferent=Net Adapter is not connected to the requested switch. PerformSwitchConnect=Connecting VM Net adapter to the right switch. + SpoofingDifferent=MAC address spoofing configuration does not match. + SpoofingConfiguredNoActionNeeded=MAC address spoofing configured. + DeviceNamingDifferent=Device naming configuration does not match. + DeviceNamingConfiguredNoActionNeeded=Device naming configured. '@ diff --git a/tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 b/tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 index e9fa23a..255fce6 100644 --- a/tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 +++ b/tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 @@ -155,12 +155,14 @@ try Describe 'DSC_VMNetworkAdapter\Set-TargetResource' { $newAdapter = [PSObject]@{ - Id = 'UniqueString' - Name = $TestAdapter.Name - SwitchName = $TestAdapter.SwitchName - VMName = 'VMName' - NetworkSetting = $networkSettingsStatic - Ensure = 'Present' + Id = 'UniqueString' + Name = $TestAdapter.Name + SwitchName = $TestAdapter.SwitchName + VMName = 'VMName' + NetworkSetting = $networkSettingsStatic + Ensure = 'Present' + DeviceNaming = 'On' + MacAddressSpoofing = 'On' } Context 'Adapter does not exist but should' { @@ -172,6 +174,7 @@ try Mock -CommandName Remove-VMNetworkAdapter Mock -CommandName Set-VMNetworkAdapterVlan Mock -CommandName Set-NetworkInformation + Mock -CommandName Set-VMNetworkAdapter It 'should not throw error' { { @@ -185,6 +188,7 @@ try Assert-MockCalled -commandName Add-VMNetworkAdapter -Exactly 1 Assert-MockCalled -commandName Remove-VMNetworkAdapter -Exactly 0 Assert-MockCalled -CommandName Set-NetworkInformation -Exactly 1 + Assert-MockCalled -CommandName Set-VMNetworkAdapter -Exactly 1 } } @@ -212,11 +216,13 @@ try Describe 'DSC_VMNetworkAdapter\Test-TargetResource' { $newAdapter = [PSObject]@{ - Id = 'UniqueString' - Name = $TestAdapter.Name - SwitchName = $TestAdapter.SwitchName - VMName = 'ManagementOS' - Ensure = 'Present' + Id = 'UniqueString' + Name = $TestAdapter.Name + SwitchName = $TestAdapter.SwitchName + VMName = 'ManagementOS' + Ensure = 'Present' + DeviceNaming = 'On' + MacAddressSpoofing = 'On' } Context 'Adapter does not exist but should' { From 4346d6f984931a62ee7782ee4b0dab6a80f16ae5 Mon Sep 17 00:00:00 2001 From: Jan-Hendrik Peters Date: Tue, 18 Apr 2023 09:35:15 +0200 Subject: [PATCH 2/9] Fix build --- azure-pipelines.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index fef6bcc..48dd895 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -154,7 +154,7 @@ stages: displayName: 'Publish Code Coverage' dependsOn: Test_Unit pool: - vmImage: 'ubuntu-latest' + vmImage: 'windows-latest' timeoutInMinutes: 0 steps: - task: DownloadPipelineArtifact@2 @@ -177,7 +177,7 @@ stages: summaryFileLocation: '$(Build.SourcesDirectory)/$(buildFolderName)/$(testResultFolderName)/JaCoCo_coverage.xml' pathToSources: '$(Build.SourcesDirectory)/$(sourceFolderName)/' - script: | - bash <(curl -s https://codecov.io/bash) -f "./$(buildFolderName)/$(testResultFolderName)/JaCoCo_coverage.xml" + bash <(curl -s https://codecov.io/bash) -f "$(Build.SourcesDirectory)/$(buildFolderName)/$(testResultFolderName)/JaCoCo_coverage.xml" displayName: 'Publish Code Coverage to Codecov.io' condition: succeededOrFailed() @@ -196,7 +196,7 @@ stages: - job: Deploy_Module displayName: 'Deploy Module' pool: - vmImage: 'ubuntu-latest' + vmImage: 'windows-latest' steps: - task: DownloadPipelineArtifact@2 displayName: 'Download Pipeline Artifact' From bd1d5682ba9848f474edf0c85725b835e0f2a85c Mon Sep 17 00:00:00 2001 From: Jan-Hendrik Peters Date: Fri, 21 Apr 2023 08:09:19 +0200 Subject: [PATCH 3/9] Revert "Fix build" This reverts commit 4346d6f984931a62ee7782ee4b0dab6a80f16ae5. --- azure-pipelines.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 48dd895..fef6bcc 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -154,7 +154,7 @@ stages: displayName: 'Publish Code Coverage' dependsOn: Test_Unit pool: - vmImage: 'windows-latest' + vmImage: 'ubuntu-latest' timeoutInMinutes: 0 steps: - task: DownloadPipelineArtifact@2 @@ -177,7 +177,7 @@ stages: summaryFileLocation: '$(Build.SourcesDirectory)/$(buildFolderName)/$(testResultFolderName)/JaCoCo_coverage.xml' pathToSources: '$(Build.SourcesDirectory)/$(sourceFolderName)/' - script: | - bash <(curl -s https://codecov.io/bash) -f "$(Build.SourcesDirectory)/$(buildFolderName)/$(testResultFolderName)/JaCoCo_coverage.xml" + bash <(curl -s https://codecov.io/bash) -f "./$(buildFolderName)/$(testResultFolderName)/JaCoCo_coverage.xml" displayName: 'Publish Code Coverage to Codecov.io' condition: succeededOrFailed() @@ -196,7 +196,7 @@ stages: - job: Deploy_Module displayName: 'Deploy Module' pool: - vmImage: 'windows-latest' + vmImage: 'ubuntu-latest' steps: - task: DownloadPipelineArtifact@2 displayName: 'Download Pipeline Artifact' From f7bb83eecd821a1e919c63fcfcc822a1c8354797 Mon Sep 17 00:00:00 2001 From: Jan-Hendrik Peters Date: Fri, 21 Apr 2023 10:28:23 +0200 Subject: [PATCH 4/9] Increase code coverage --- .../DSC_VMNetworkAdapter/DSC_VMNetworkAdapter.psm1 | 13 +++++++++---- tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 | 2 ++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/source/DSCResources/DSC_VMNetworkAdapter/DSC_VMNetworkAdapter.psm1 b/source/DSCResources/DSC_VMNetworkAdapter/DSC_VMNetworkAdapter.psm1 index 373c73b..9f8c60d 100644 --- a/source/DSCResources/DSC_VMNetworkAdapter/DSC_VMNetworkAdapter.psm1 +++ b/source/DSCResources/DSC_VMNetworkAdapter/DSC_VMNetworkAdapter.psm1 @@ -221,12 +221,12 @@ function Set-TargetResource if ($netAdapterExists.DynamicMacAddressEnabled) { Write-Verbose -Message $script:localizedData.EnableStaticMacAddress - $updateMacAddress = $true + $updateAdapter = $true } elseif ($MacAddress -ne $netAdapterExists.StaicMacAddress) { Write-Verbose -Message $script:localizedData.EnableStaticMacAddress - $updateMacAddress = $true + $updateAdapter = $true } } else @@ -234,17 +234,22 @@ function Set-TargetResource if (-not $netAdapterExists.DynamicMacAddressEnabled) { Write-Verbose -Message $script:localizedData.EnableDynamicMacAddress - $updateMacAddress = $true + $updateAdapter = $true } } + if ($netAdapterExists.DeviceNaming -ne $DeviceNaming) + { + $updateAdapter = $true + } + if ($netAdapterExists.SwitchName -ne $SwitchName) { Write-Verbose -Message $script:localizedData.PerformSwitchConnect Connect-VMNetworkAdapter -VMNetworkAdapter $netAdapterExists -SwitchName $SwitchName -ErrorAction Stop -Verbose } - if (($updateMacAddress)) + if (($updateAdapter)) { Write-Verbose -Message $script:localizedData.PerformVMNetModify diff --git a/tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 b/tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 index 255fce6..b1b4d3d 100644 --- a/tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 +++ b/tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 @@ -61,6 +61,8 @@ try $MockAdapter.VMName = $MockHostAdapter.VMName $MockAdapter.IsManagementOs = $true $MockAdapter.MacAddress = '14FEB5C6CE98' + $MockAdapter.DeviceNaming = 'Off' + $MockAdapter.MacAddressSpoofing = 'Off' $MockAdapterVlanUntagged = [PSObject]@{ OperationMode = 'Untagged' From 913243d67b591fbcd791f02c1c1beea529a221dc Mon Sep 17 00:00:00 2001 From: Jan-Hendrik Peters Date: Fri, 21 Apr 2023 10:41:42 +0200 Subject: [PATCH 5/9] Update stub to include DeviceNaming property --- tests/Unit/Stubs/Hyper-V.stubs.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Unit/Stubs/Hyper-V.stubs.psm1 b/tests/Unit/Stubs/Hyper-V.stubs.psm1 index 5f070ed..f0af410 100644 --- a/tests/Unit/Stubs/Hyper-V.stubs.psm1 +++ b/tests/Unit/Stubs/Hyper-V.stubs.psm1 @@ -1254,6 +1254,7 @@ namespace Microsoft.HyperV.PowerShell { // Manually added properties public System.String MacAddress { get; set; } + public Microsoft.HyperV.PowerShell.OnOffState DeviceNaming { get; set; } // Property public System.String SwitchName { get; set; } @@ -20498,4 +20499,3 @@ Wait-VM [-VM] [-AsJob] [-Passthru] [-For ] [-Del throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } } - From 30c9c04a9d3381c344fdcd24f0fce99e842cd3cd Mon Sep 17 00:00:00 2001 From: Jan-Hendrik Peters Date: Fri, 21 Apr 2023 11:26:50 +0200 Subject: [PATCH 6/9] Attempt to improve code coverage --- .../DSC_VMHyperV/DSC_VMHyperV.psm1 | 2 +- .../DSC_VMNetworkAdapter.psm1 | 2 +- tests/Unit/DSC_VMHyperV.Tests.ps1 | 6 +- tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 | 130 ++++++++++++++++++ tests/Unit/Stubs/Hyper-V.stubs.psm1 | 8 +- 5 files changed, 140 insertions(+), 8 deletions(-) diff --git a/source/DSCResources/DSC_VMHyperV/DSC_VMHyperV.psm1 b/source/DSCResources/DSC_VMHyperV/DSC_VMHyperV.psm1 index 112942f..a233de5 100644 --- a/source/DSCResources/DSC_VMHyperV/DSC_VMHyperV.psm1 +++ b/source/DSCResources/DSC_VMHyperV/DSC_VMHyperV.psm1 @@ -362,7 +362,7 @@ function Set-TargetResource if ($nic.SwitchName -ne $switch) { Write-Verbose -Message ($script:localizedData.VMPropertyShouldBe -f 'NIC', $switch, $nic.SwitchName) - $nic | Connect-VMNetworkAdapter -SwitchName $switch + Connect-VMNetworkAdapter -VMNetworkAdapter $nic -SwitchName $switch Write-Verbose -Message ($script:localizedData.VMPropertySet -f 'NIC', $switch) } } diff --git a/source/DSCResources/DSC_VMNetworkAdapter/DSC_VMNetworkAdapter.psm1 b/source/DSCResources/DSC_VMNetworkAdapter/DSC_VMNetworkAdapter.psm1 index 9f8c60d..efa7c08 100644 --- a/source/DSCResources/DSC_VMNetworkAdapter/DSC_VMNetworkAdapter.psm1 +++ b/source/DSCResources/DSC_VMNetworkAdapter/DSC_VMNetworkAdapter.psm1 @@ -223,7 +223,7 @@ function Set-TargetResource Write-Verbose -Message $script:localizedData.EnableStaticMacAddress $updateAdapter = $true } - elseif ($MacAddress -ne $netAdapterExists.StaicMacAddress) + elseif ($MacAddress -ne $netAdapterExists.MacAddress) { Write-Verbose -Message $script:localizedData.EnableStaticMacAddress $updateAdapter = $true diff --git a/tests/Unit/DSC_VMHyperV.Tests.ps1 b/tests/Unit/DSC_VMHyperV.Tests.ps1 index 13097b0..e1b4e1c 100644 --- a/tests/Unit/DSC_VMHyperV.Tests.ps1 +++ b/tests/Unit/DSC_VMHyperV.Tests.ps1 @@ -46,13 +46,13 @@ try $StubVMConfig = New-Item -Path 'TestDrive:\TestVM.xml' -ItemType File # Mock the class VMNetworkAdapter to support piping to cmdlet Connect-VMNetworkAdapter - $stubNIC1 = [Microsoft.HyperV.PowerShell.VMNetworkAdapter]::CreateTypeInstance() + $stubNIC1 = [Microsoft.HyperV.PowerShell.VMNetworkAdapterBase]::CreateTypeInstance() $stubNIC1.SwitchName = 'Test Switch 1' $stubNIC1.MacAddress = 'AA-BB-CC-DD-EE-FF' $stubNIC1.IpAddresses = @('192.168.0.1', '10.0.0.1') # Mock the class VMNetworkAdapter to support piping to cmdlet Connect-VMNetworkAdapter - $stubNIC2 = [Microsoft.HyperV.PowerShell.VMNetworkAdapter]::CreateTypeInstance() + $stubNIC2 = [Microsoft.HyperV.PowerShell.VMNetworkAdapterBase]::CreateTypeInstance() $stubNIC2.SwitchName = 'Test Switch 2' $stubNIC2.MacAddress = 'AA-BB-CC-DD-EE-FE' $stubNIC2.IpAddresses = @('192.168.1.1') @@ -602,7 +602,7 @@ try Mock -CommandName Stop-VM -MockWith { return $true } # requires output to be able to pipe something into Remove-VM Mock -CommandName Remove-VM -MockWith { return $true } Mock -CommandName Set-VMNetworkAdapter -MockWith { return $true } - Mock -CommandName Get-VMNetworkAdapter -MockWith { return $stubVM.NetworkAdapters.IpAddresses } + Mock -CommandName Get-VMNetworkAdapter -MockWith { return $stubVM.NetworkAdapters } Mock -CommandName Set-VMState -MockWith { return $true } Mock -CommandName Set-VMMemory diff --git a/tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 b/tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 index b1b4d3d..9ebd550 100644 --- a/tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 +++ b/tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 @@ -63,6 +63,17 @@ try $MockAdapter.MacAddress = '14FEB5C6CE98' $MockAdapter.DeviceNaming = 'Off' $MockAdapter.MacAddressSpoofing = 'Off' + $MockAdapter.DynamicMacAddressEnabled = $false + + $dynamicMockAdapter = [Microsoft.HyperV.PowerShell.VMNetworkAdapterBase]::CreateTypeInstance() + $dynamicMockAdapter.Name = $MockHostAdapter.Name + $dynamicMockAdapter.SwitchName = $MockHostAdapter.SwitchName + $dynamicMockAdapter.VMName = $MockHostAdapter.VMName + $dynamicMockAdapter.IsManagementOs = $true + $dynamicMockAdapter.MacAddress = '14FEB5C6CE98' + $dynamicMockAdapter.DeviceNaming = 'Off' + $dynamicMockAdapter.MacAddressSpoofing = 'Off' + $dynamicMockAdapter.DynamicMacAddressEnabled = $true $MockAdapterVlanUntagged = [PSObject]@{ OperationMode = 'Untagged' @@ -214,6 +225,82 @@ try Assert-MockCalled -CommandName Set-VMNetworkAdapterVlan -Exactly 0 } } + + Context 'Adapter exists but hw address is different' { + Mock -CommandName Get-VMNetworkAdapter -MockWith { $MockAdapter } + Mock -CommandName Set-VMNetworkAdapter + Mock -CommandName Get-VMNetworkAdapterVlan + + It 'should return false' { + $updateAdapter = $newAdapter.Clone() + $updateAdapter.Remove('NetworkSetting') + $updateAdapter.VMName = "VMName" + $updateAdapter.MacAddress = '14FEB5C6CE99' + + { Set-TargetResource @updateAdapter } | Should -Not -throw + } + It 'should call expected Mocks' { + Assert-MockCalled -commandName Get-VMNetworkAdapter -Exactly 1 + Assert-MockCalled -commandName Set-VMNetworkAdapter -Exactly 1 + } + } + Context 'Adapter exists but hw address is different and dynamic hw address is enabled' { + Mock -CommandName Get-VMNetworkAdapter -MockWith { $dynamicMockAdapter } + Mock -CommandName Set-VMNetworkAdapter + Mock -CommandName Get-VMNetworkAdapterVlan + + It 'should return false' { + $updateAdapter = $newAdapter.Clone() + $updateAdapter.Remove('NetworkSetting') + $updateAdapter.VMName = "VMName" + $updateAdapter.MacAddress = '14FEB5C6CE99' + + { Set-TargetResource @updateAdapter } | Should -Not -throw + } + It 'should call expected Mocks' { + Assert-MockCalled -commandName Get-VMNetworkAdapter -Exactly 1 + Assert-MockCalled -commandName Set-VMNetworkAdapter -Exactly 1 + } + } + + Context 'Adapter exists but dynamic hw address setting is different' { + Mock -CommandName Get-VMNetworkAdapter -MockWith { $MockAdapter } + Mock -CommandName Set-VMNetworkAdapter + Mock -CommandName Get-VMNetworkAdapterVlan + + It 'should return false' { + $updateAdapter = $newAdapter.Clone() + $updateAdapter.Remove('NetworkSetting') + $updateAdapter.VMName = "VMName" + + { Set-TargetResource @updateAdapter } | Should -Not -throw + } + It 'should call expected Mocks' { + Assert-MockCalled -commandName Get-VMNetworkAdapter -Exactly 1 + Assert-MockCalled -commandName Set-VMNetworkAdapter -Exactly 1 + } + } + + Context 'Adapter exists but connected to wrong switch' { + Mock -CommandName Get-VMNetworkAdapter -MockWith { $MockAdapter } + Mock -CommandName Set-VMNetworkAdapter + Mock -CommandName Connect-VMNetworkAdapter + Mock -CommandName Get-VMNetworkAdapterVlan + + It 'should return false' { + $updateAdapter = $newAdapter.Clone() + $updateAdapter.Remove('NetworkSetting') + $updateAdapter.VMName = "VMName" + $updateAdapter.SwitchName = "IAmAWrongSwitch" + + { Set-TargetResource @updateAdapter } | Should -Not -throw + } + It 'should call expected Mocks' { + Assert-MockCalled -commandName Get-VMNetworkAdapter -Exactly 1 + Assert-MockCalled -commandName Connect-VMNetworkAdapter -Exactly 1 + Assert-MockCalled -commandName Set-VMNetworkAdapter -Exactly 1 + } + } } Describe 'DSC_VMNetworkAdapter\Test-TargetResource' { @@ -349,6 +436,49 @@ try Assert-MockCalled -commandName Get-NetworkInformation -Exactly 0 } } + + Context 'Adapter exists but hw address is different' { + Mock -CommandName Get-VMNetworkAdapter -MockWith { $MockAdapter } + + It 'should return false' { + $updateAdapter = $newAdapter.Clone() + $updateAdapter.VMName = "VMName" + $updateAdapter.MacAddress = '14FEB5C6CE99' + + Test-TargetResource @updateAdapter | Should -Be $false + } + It 'should call expected Mocks' { + Assert-MockCalled -commandName Get-VMNetworkAdapter -Exactly 1 + } + } + Context 'Adapter exists but hw address is different and dynamic hw address is enabled' { + Mock -CommandName Get-VMNetworkAdapter -MockWith { $dynamicMockAdapter } + + It 'should return false' { + $updateAdapter = $newAdapter.Clone() + $updateAdapter.VMName = "VMName" + $updateAdapter.MacAddress = '14FEB5C6CE99' + + Test-TargetResource @updateAdapter | Should -Be $false + } + It 'should call expected Mocks' { + Assert-MockCalled -commandName Get-VMNetworkAdapter -Exactly 1 + } + } + + Context 'Adapter exists but dynamic hw address setting is different' { + Mock -CommandName Get-VMNetworkAdapter -MockWith { $MockAdapter } + + It 'should return false' { + $updateAdapter = $newAdapter.Clone() + $updateAdapter.VMName = "VMName" + + Test-TargetResource @updateAdapter | Should -Be $false + } + It 'should call expected Mocks' { + Assert-MockCalled -commandName Get-VMNetworkAdapter -Exactly 1 + } + } } } diff --git a/tests/Unit/Stubs/Hyper-V.stubs.psm1 b/tests/Unit/Stubs/Hyper-V.stubs.psm1 index f0af410..293fe97 100644 --- a/tests/Unit/Stubs/Hyper-V.stubs.psm1 +++ b/tests/Unit/Stubs/Hyper-V.stubs.psm1 @@ -340,7 +340,7 @@ namespace Microsoft.HyperV.PowerShell public System.DateTime CreationTime { get; set; } public System.Guid Id { get; set; } public System.String Name { get; set; } - public System.Collections.Generic.List NetworkAdapters { get; set; } + public System.Collections.Generic.List NetworkAdapters { get; set; } public Microsoft.Management.Infrastructure.CimSession CimSession { get; set; } public System.String ComputerName { get; set; } public System.Boolean IsDeleted { get; set; } @@ -1255,6 +1255,8 @@ namespace Microsoft.HyperV.PowerShell // Manually added properties public System.String MacAddress { get; set; } public Microsoft.HyperV.PowerShell.OnOffState DeviceNaming { get; set; } + public System.Boolean DynamicMacAddressEnabled { get; set; } + public System.String[] IPAddresses { get; set; } // Property public System.String SwitchName { get; set; } @@ -4862,13 +4864,13 @@ function Connect-VMNetworkAdapter { #> [CmdletBinding(DefaultParameterSetName='Name_SwitchName', SupportsShouldProcess=$true, ConfirmImpact='Medium')] - [OutputType([Microsoft.HyperV.PowerShell.VMNetworkAdapter])] + [OutputType([Microsoft.HyperV.PowerShell.VMNetworkAdapterBase])] param ( [Parameter(ParameterSetName='Object_SwitchName', Mandatory=$true, Position=0, ValueFromPipeline=$true)] [Parameter(ParameterSetName='Object_SwitchObject', Mandatory=$true, Position=0, ValueFromPipeline=$true)] [Parameter(ParameterSetName='Object_UseAutomaticConnection', Mandatory=$true, Position=0, ValueFromPipeline=$true)] [ValidateNotNullOrEmpty()] - [Microsoft.HyperV.PowerShell.VMNetworkAdapter[]] + [Microsoft.HyperV.PowerShell.VMNetworkAdapterBase[]] ${VMNetworkAdapter}, [Parameter(ParameterSetName='Name_SwitchObject', Position=1)] From e879c223c418b7c0efb837b4c19267949ac95806 Mon Sep 17 00:00:00 2001 From: Jan-Hendrik Peters Date: Fri, 21 Apr 2023 13:28:48 +0200 Subject: [PATCH 7/9] attempt to increase code coverage --- tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 | 73 ++++++++++++++++++++++- 1 file changed, 71 insertions(+), 2 deletions(-) diff --git a/tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 b/tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 index 9ebd550..0b4f00d 100644 --- a/tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 +++ b/tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 @@ -68,7 +68,7 @@ try $dynamicMockAdapter = [Microsoft.HyperV.PowerShell.VMNetworkAdapterBase]::CreateTypeInstance() $dynamicMockAdapter.Name = $MockHostAdapter.Name $dynamicMockAdapter.SwitchName = $MockHostAdapter.SwitchName - $dynamicMockAdapter.VMName = $MockHostAdapter.VMName + $dynamicMockAdapter.VMName = 'VMName' $dynamicMockAdapter.IsManagementOs = $true $dynamicMockAdapter.MacAddress = '14FEB5C6CE98' $dynamicMockAdapter.DeviceNaming = 'Off' @@ -453,6 +453,7 @@ try } Context 'Adapter exists but hw address is different and dynamic hw address is enabled' { Mock -CommandName Get-VMNetworkAdapter -MockWith { $dynamicMockAdapter } + Mock -CommandName Get-VMNetworkAdapterVlan It 'should return false' { $updateAdapter = $newAdapter.Clone() @@ -467,11 +468,79 @@ try } Context 'Adapter exists but dynamic hw address setting is different' { - Mock -CommandName Get-VMNetworkAdapter -MockWith { $MockAdapter } + Mock -CommandName Get-VMNetworkAdapter -MockWith { $dynamicMockAdapter } + Mock -CommandName Get-VMNetworkAdapterVlan It 'should return false' { $updateAdapter = $newAdapter.Clone() $updateAdapter.VMName = "VMName" + $updateAdapter.MacAddress = '14FEB5C6CE98' + + Test-TargetResource @updateAdapter | Should -Be $false + } + It 'should call expected Mocks' { + Assert-MockCalled -commandName Get-VMNetworkAdapter -Exactly 1 + } + } + + Context 'Adapter exists and device naming setting OK' { + Mock -CommandName Get-VMNetworkAdapter -MockWith { $dynamicMockAdapter } + Mock -CommandName Get-VMNetworkAdapterVlan + + It 'should return true' { + $updateAdapter = $newAdapter.Clone() + $updateAdapter.VMName = "VMName" + $updateAdapter.DeviceNaming = "Off" + + Test-TargetResource @updateAdapter | Should -Be $true + } + It 'should call expected Mocks' { + Assert-MockCalled -commandName Get-VMNetworkAdapter -Exactly 1 + } + } + + Context 'Adapter exists and device naming setting different' { + Mock -CommandName Get-VMNetworkAdapter -MockWith { $dynamicMockAdapter } + Mock -CommandName Get-VMNetworkAdapterVlan + + It 'should return false' { + $updateAdapter = $newAdapter.Clone() + $updateAdapter.VMName = "VMName" + $updateAdapter.DeviceNaming = "On" + $updateAdapter.MacAddress = '14FEB5C6CE98' + + Test-TargetResource @updateAdapter | Should -Be $false + } + It 'should call expected Mocks' { + Assert-MockCalled -commandName Get-VMNetworkAdapter -Exactly 1 + } + } + + Context 'Adapter exists and spoofing setting OK' { + Mock -CommandName Get-VMNetworkAdapter -MockWith { $dynamicMockAdapter } + Mock -CommandName Get-VMNetworkAdapterVlan + + It 'should return true' { + $updateAdapter = $newAdapter.Clone() + $updateAdapter.VMName = "VMName" + $updateAdapter.MacAddressSpoofing = "Off" + + Test-TargetResource @updateAdapter | Should -Be $true + } + It 'should call expected Mocks' { + Assert-MockCalled -commandName Get-VMNetworkAdapter -Exactly 1 + } + } + + Context 'Adapter exists and spoofing setting different' { + Mock -CommandName Get-VMNetworkAdapter -MockWith { $dynamicMockAdapter } + Mock -CommandName Get-VMNetworkAdapterVlan + + It 'should return false' { + $updateAdapter = $newAdapter.Clone() + $updateAdapter.VMName = "VMName" + $updateAdapter.MacAddressSpoofing = "On" + $updateAdapter.MacAddress = '14FEB5C6CE98' Test-TargetResource @updateAdapter | Should -Be $false } From ad43eac5116aa295911af0afaed172294a584776 Mon Sep 17 00:00:00 2001 From: Jan-Hendrik Peters Date: Mon, 24 Apr 2023 09:46:56 +0200 Subject: [PATCH 8/9] Attempt to increase code coverage --- .../DSC_VMNetworkAdapter/DSC_VMNetworkAdapter.psm1 | 8 +++----- .../en-US/DSC_VMNetworkAdapter.strings.psd1 | 1 + tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 | 6 ++++++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/source/DSCResources/DSC_VMNetworkAdapter/DSC_VMNetworkAdapter.psm1 b/source/DSCResources/DSC_VMNetworkAdapter/DSC_VMNetworkAdapter.psm1 index efa7c08..37c1f63 100644 --- a/source/DSCResources/DSC_VMNetworkAdapter/DSC_VMNetworkAdapter.psm1 +++ b/source/DSCResources/DSC_VMNetworkAdapter/DSC_VMNetworkAdapter.psm1 @@ -596,8 +596,7 @@ function Test-TargetResource } else { - Write-Verbose -Message $script:localizedData.VMNetAdapterExistsNoActionNeeded - return $true + Write-Verbose -Message $script:localizedData.SwitchIsCorrect } if ($netAdapterExists.MacAddressSpoofing -ne $MacAddressSpoofing) @@ -608,8 +607,6 @@ function Test-TargetResource else { Write-Verbose -Message $script:localizedData.SpoofingConfiguredNoActionNeeded - return $true - } if ($netAdapterExists.DeviceNaming -ne $DeviceNaming) @@ -620,8 +617,9 @@ function Test-TargetResource else { Write-Verbose -Message $script:localizedData.DeviceNamingConfiguredNoActionNeeded - return $true } + + return $true } else { diff --git a/source/DSCResources/DSC_VMNetworkAdapter/en-US/DSC_VMNetworkAdapter.strings.psd1 b/source/DSCResources/DSC_VMNetworkAdapter/en-US/DSC_VMNetworkAdapter.strings.psd1 index cdc9c77..a6ad723 100644 --- a/source/DSCResources/DSC_VMNetworkAdapter/en-US/DSC_VMNetworkAdapter.strings.psd1 +++ b/source/DSCResources/DSC_VMNetworkAdapter/en-US/DSC_VMNetworkAdapter.strings.psd1 @@ -26,6 +26,7 @@ ConvertFrom-StringData @' VMNetAdapterExistsShouldRemove=VM Network Adapter Exists. It will be removed. VMNetAdapterDoesNotExistNoActionNeeded=VM Network adapter does not exist. No action needed. SwitchIsDifferent=Net Adapter is not connected to the requested switch. + SwitchIsCorrect=Net Adapter is connected to the requested switch. PerformSwitchConnect=Connecting VM Net adapter to the right switch. SpoofingDifferent=MAC address spoofing configuration does not match. SpoofingConfiguredNoActionNeeded=MAC address spoofing configured. diff --git a/tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 b/tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 index 0b4f00d..9f5cb2b 100644 --- a/tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 +++ b/tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 @@ -361,6 +361,8 @@ try $updateAdapter.VMName = "VMName" $updateAdapter.MacAddress = '14FEB5C6CE98' $updateAdapter.VlanId = '1' + $updateAdapter.DeviceNaming = 'Off' + $updateAdapter.MacAddressSpoofing = 'Off' Test-TargetResource @updateAdapter | Should -Be $true } It 'should call expected Mocks' { @@ -491,6 +493,7 @@ try $updateAdapter = $newAdapter.Clone() $updateAdapter.VMName = "VMName" $updateAdapter.DeviceNaming = "Off" + $updateAdapter.MacAddressSpoofing = "Off" Test-TargetResource @updateAdapter | Should -Be $true } @@ -507,6 +510,7 @@ try $updateAdapter = $newAdapter.Clone() $updateAdapter.VMName = "VMName" $updateAdapter.DeviceNaming = "On" + $updateAdapter.MacAddressSpoofing = "Off" $updateAdapter.MacAddress = '14FEB5C6CE98' Test-TargetResource @updateAdapter | Should -Be $false @@ -524,6 +528,7 @@ try $updateAdapter = $newAdapter.Clone() $updateAdapter.VMName = "VMName" $updateAdapter.MacAddressSpoofing = "Off" + $updateAdapter.DeviceNaming = "Off" Test-TargetResource @updateAdapter | Should -Be $true } @@ -540,6 +545,7 @@ try $updateAdapter = $newAdapter.Clone() $updateAdapter.VMName = "VMName" $updateAdapter.MacAddressSpoofing = "On" + $updateAdapter.DeviceNaming = "Off" $updateAdapter.MacAddress = '14FEB5C6CE98' Test-TargetResource @updateAdapter | Should -Be $false From 517cf60c69cd81ad57fedd0fad2f2c65bd2dd375 Mon Sep 17 00:00:00 2001 From: Jan-Hendrik Peters Date: Mon, 24 Apr 2023 09:59:30 +0200 Subject: [PATCH 9/9] Attempt to increase code coverage --- tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 b/tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 index 9f5cb2b..77b704e 100644 --- a/tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 +++ b/tests/Unit/DSC_VMNetworkAdapter.Tests.ps1 @@ -511,7 +511,6 @@ try $updateAdapter.VMName = "VMName" $updateAdapter.DeviceNaming = "On" $updateAdapter.MacAddressSpoofing = "Off" - $updateAdapter.MacAddress = '14FEB5C6CE98' Test-TargetResource @updateAdapter | Should -Be $false } @@ -546,7 +545,6 @@ try $updateAdapter.VMName = "VMName" $updateAdapter.MacAddressSpoofing = "On" $updateAdapter.DeviceNaming = "Off" - $updateAdapter.MacAddress = '14FEB5C6CE98' Test-TargetResource @updateAdapter | Should -Be $false }