Skip to content

Commit

Permalink
Add inclusion in Hyper-V cluster
Browse files Browse the repository at this point in the history
  • Loading branch information
nyanhp committed Apr 21, 2023
1 parent 0e5c3d9 commit 963f217
Show file tree
Hide file tree
Showing 7 changed files with 8,002 additions and 281 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
VMHyperV
- Enable adding VMs to failover cluster.

## [3.18.0] - 2022-06-04

Expand Down
605 changes: 327 additions & 278 deletions source/DSCResources/DSC_VMHyperV/DSC_VMHyperV.psm1

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions source/DSCResources/DSC_VMHyperV/DSC_VMHyperV.schema.mof
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class DSC_VMHyperV : OMI_BaseResource
[Write, Description("Specifies if Secure Boot should be enabled for Generation 2 virtual machines. **Only supports generation 2 virtual machines**. Default value is `$true`.")] Boolean SecureBoot;
[Write, Description("Enable Guest Service Interface for the VM. The default value is `$false`.")] Boolean EnableGuestService;
[Write, Description("Enable AutomaticCheckpoints for the VM.")] Boolean AutomaticCheckpointsEnabled;
[Write, Description("Enable inclusion in running failover cluster if present.")] Boolean IsClustered;
[Read, Description("Returns the unique ID for the VM.")] String ID;
[Read, Description("Returns the current status of the VM.")] String Status;
[Read, Description("Returns the current CPU usage of the VM.")] Uint32 CPUUsage;
Expand Down
5 changes: 5 additions & 0 deletions source/DSCResources/DSC_VMHyperV/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ The following properties **cannot** be changed after VM creation:
* Path
* Generation

If IsClustered is enabled, please use PsDscRunAsCredential with a
privileged account in order to add a VM to the cluster that the
Hyper-V host is a member of.

## Requirements

* The Hyper-V Role has to be installed on the machine.
* The Hyper-V PowerShell module has to be installed on the machine.
* If VMs should be added to a Failover Cluster, the FailoverClusters module needs to be installed on the machine.
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,7 @@ ConvertFrom-StringData @'
VMPropertySet = VM property '{0}' is '{1}'.
VMPropertiesUpdated = VM '{0}' properties have been updated.
QueryingVM = Querying VM '{0}'.
NotAddedToCluster = VM '{0}' does not appear to be added as a cluster role when it should be.
RemoveClusterGroup = Removing VM '{0}' from cluster.
AddingToCluster = Adding VM '{0}' to cluster.
'@
193 changes: 190 additions & 3 deletions tests/Unit/DSC_VMHyperV.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ function Invoke-TestSetup

# Import the stub functions.
Import-Module -Name "$PSScriptRoot/Stubs/Hyper-V.stubs.psm1" -Force
Import-Module -Name "$PSScriptRoot/Stubs/FailoverClusters" -Force
}

function Invoke-TestCleanup
Expand All @@ -32,7 +33,7 @@ Invoke-TestSetup
try
{
InModuleScope $script:dscResourceName {
Describe 'xVMHyper-V' {
Describe 'VMHyperV' {
$null = New-Item -Path 'TestDrive:\TestVM.vhdx' -ItemType File
$null = New-Item -Path 'TestDrive:\TestVM.vhd' -ItemType File

Expand Down Expand Up @@ -80,6 +81,7 @@ try
$stubVM.DynamicMemoryEnabled = $true
$stubVM.Notes = ''
$stubVM.State = 'Running'
$stubVM.IsClustered = $false
$stubVM.NetworkAdapters = @(
$stubNIC1,
$stubNIC2
Expand Down Expand Up @@ -109,6 +111,7 @@ try
$stubVM.DynamicMemoryEnabled = $true
$stubVM.Notes = ''
$stubVM.State = 'Off'
$stubVM.IsClustered = $false
$stubVM.NetworkAdapters = @(
$stubNIC1,
$stubNIC2
Expand Down Expand Up @@ -138,6 +141,7 @@ try
$stubVM.DynamicMemoryEnabled = $true
$stubVM.Notes = ''
$stubVM.State = 'Paused'
$stubVM.IsClustered = $false
$stubVM.NetworkAdapters = @(
$stubNIC1,
$stubNIC2
Expand Down Expand Up @@ -171,6 +175,7 @@ try
$stubVM1.DynamicMemoryEnabled = $true
$stubVM1.Notes = ''
$stubVM1.State = 'Off'
$stubVM.IsClustered = $false
$stubVM1.NetworkAdapters = @(
$stubNIC1,
$stubNIC2
Expand Down Expand Up @@ -227,6 +232,7 @@ try
$stubVM.DynamicMemoryEnabled = $true
$stubVM.Notes = ''
$stubVM.State = 'Running'
$stubVM.IsClustered = $false
$stubVM.NetworkAdapters = @(
$stubNIC1,
$stubNIC2
Expand Down Expand Up @@ -255,6 +261,7 @@ try
$stubVM.DynamicMemoryEnabled = $true
$stubVM.Notes = ''
$stubVM.State = 'Running'
$stubVM.IsClustered = $false
$stubVM.NetworkAdapters = @(
$stubNIC1,
$stubNIC2
Expand Down Expand Up @@ -283,6 +290,7 @@ try
$stubVM.DynamicMemoryEnabled = $true
$stubVM.Notes = ''
$stubVM.State = 'Running'
$stubVM.IsClustered = $false
$stubVM.AutomaticCheckpointsEnabled = $true
$stubVM.NetworkAdapters = @(
$stubNIC1,
Expand Down Expand Up @@ -312,6 +320,7 @@ try
$stubVM.DynamicMemoryEnabled = $true
$stubVM.Notes = ''
$stubVM.State = 'Running'
$stubVM.IsClustered = $false
$stubVM.AutomaticCheckpointsEnabled = $false
$stubVM.NetworkAdapters = @(
$stubNIC1,
Expand Down Expand Up @@ -341,6 +350,127 @@ try
$stubVM.DynamicMemoryEnabled = $true
$stubVM.Notes = ''
$stubVM.State = 'Running'
$stubVM.IsClustered = $false
$stubVM.NetworkAdapters = @(
$stubNIC1,
$stubNIC2
)

return $stubVM
}

Mock -CommandName Get-VM -ParameterFilter { $Name -eq 'ClusteredExistingVmNotInClusterPresent' } -MockWith {
$stubVM = [Microsoft.HyperV.PowerShell.VirtualMachine]::CreateTypeInstance()
$stubVM.Name = 'ClusteredExistingVmNotInClusterPresent'
$stubVM.HardDrives = @(
$stubVhdxDisk,
$stubVhdDisk
)
$stubVM.Path = $StubVMConfig.FullPath
$stubVM.Generation = 1
$stubVM.MemoryStartup = 512MB
$stubVM.MemoryMinimum = 128MB
$stubVM.MemoryMaximum = 4096MB
$stubVM.ProcessorCount = 1
$stubVM.ID = $mockVmGuid
$stubVM.CPUUsage = 10
$stubVM.MemoryAssigned = 512MB
$stubVM.Uptime = New-TimeSpan -Hours 12
$stubVM.CreationTime = (Get-Date).AddHours(-12)
$stubVM.DynamicMemoryEnabled = $true
$stubVM.Notes = ''
$stubVM.State = 'Running'
$stubVM.IsClustered = $false
$stubVM.NetworkAdapters = @(
$stubNIC1,
$stubNIC2
)

return $stubVM
}

Mock -CommandName Get-VM -ParameterFilter { $Name -eq 'ClusteredExistingVmInClusterPresent' } -MockWith {
$stubVM = [Microsoft.HyperV.PowerShell.VirtualMachine]::CreateTypeInstance()
$stubVM.Name = 'ClusteredExistingVmInClusterPresent'
$stubVM.HardDrives = @(
$stubVhdxDisk,
$stubVhdDisk
)
$stubVM.Path = $StubVMConfig.FullPath
$stubVM.Generation = 1
$stubVM.MemoryStartup = 512MB
$stubVM.MemoryMinimum = 128MB
$stubVM.MemoryMaximum = 4096MB
$stubVM.ProcessorCount = 1
$stubVM.ID = $mockVmGuid
$stubVM.CPUUsage = 10
$stubVM.MemoryAssigned = 512MB
$stubVM.Uptime = New-TimeSpan -Hours 12
$stubVM.CreationTime = (Get-Date).AddHours(-12)
$stubVM.DynamicMemoryEnabled = $true
$stubVM.Notes = ''
$stubVM.State = 'Running'
$stubVM.IsClustered = $true
$stubVM.NetworkAdapters = @(
$stubNIC1,
$stubNIC2
)

return $stubVM
}

Mock -CommandName Get-VM -ParameterFilter { $Name -eq 'ClusteredNewVmPresent' } -MockWith {
$stubVM = [Microsoft.HyperV.PowerShell.VirtualMachine]::CreateTypeInstance()
$stubVM.Name = 'ClusteredNewVmPresent'
$stubVM.HardDrives = @(
$stubVhdxDisk,
$stubVhdDisk
)
$stubVM.Path = $StubVMConfig.FullPath
$stubVM.Generation = 1
$stubVM.MemoryStartup = 512MB
$stubVM.MemoryMinimum = 128MB
$stubVM.MemoryMaximum = 4096MB
$stubVM.ProcessorCount = 1
$stubVM.ID = $mockVmGuid
$stubVM.CPUUsage = 10
$stubVM.MemoryAssigned = 512MB
$stubVM.Uptime = New-TimeSpan -Hours 12
$stubVM.CreationTime = (Get-Date).AddHours(-12)
$stubVM.DynamicMemoryEnabled = $true
$stubVM.Notes = ''
$stubVM.State = 'Running'
$stubVM.IsClustered = $false
$stubVM.NetworkAdapters = @(
$stubNIC1,
$stubNIC2
)

return $stubVM
}

Mock -CommandName Get-VM -ParameterFilter { $Name -eq 'ClusteredExistingVmInClusterAbsent' } -MockWith {
$stubVM = [Microsoft.HyperV.PowerShell.VirtualMachine]::CreateTypeInstance()
$stubVM.Name = 'ClusteredExistingVmInClusterAbsent'
$stubVM.HardDrives = @(
$stubVhdxDisk,
$stubVhdDisk
)
$stubVM.Path = $StubVMConfig.FullPath
$stubVM.Generation = 1
$stubVM.MemoryStartup = 512MB
$stubVM.MemoryMinimum = 128MB
$stubVM.MemoryMaximum = 4096MB
$stubVM.ProcessorCount = 1
$stubVM.ID = $mockVmGuid
$stubVM.CPUUsage = 10
$stubVM.MemoryAssigned = 512MB
$stubVM.Uptime = New-TimeSpan -Hours 12
$stubVM.CreationTime = (Get-Date).AddHours(-12)
$stubVM.DynamicMemoryEnabled = $true
$stubVM.Notes = ''
$stubVM.State = 'Running'
$stubVM.IsClustered = $true
$stubVM.NetworkAdapters = @(
$stubNIC1,
$stubNIC2
Expand All @@ -360,6 +490,7 @@ try
}

Mock -CommandName Get-Module -ParameterFilter { ($Name -eq 'Hyper-V') -and ($ListAvailable -eq $true) } -MockWith { return $true }
Mock -CommandName Get-Module -ParameterFilter { ($Name -eq 'FailoverClusters') -and ($ListAvailable -eq $true) } -MockWith { return $true }
Mock -CommandName Get-VhdHierarchy -ParameterFilter { $VhdPath.EndsWith('.vhd') } -MockWith {
# Return single Vhd chain for .vhds
return @($stubVhdDisk.Path)
Expand Down Expand Up @@ -399,10 +530,19 @@ try
$targetResource = Get-TargetResource -Name 'VMWithAutomaticCheckpoints' -VhdPath $stubVhdxDisk.Path
$targetResource.ContainsKey('AutomaticCheckpointsEnabled') | Should -Be $true
}
It 'Hash table contains key IsClustered' {
$targetResource = Get-TargetResource -Name 'ClusteredExistingVmInClusterPresent' -VhdPath $stubVhdxDisk.Path
$targetResource.ContainsKey('IsClustered') | Should -Be $true
}
It 'throws when Hyper-V Tools are not installed' {
# This test needs to be the last in the Context otherwise all subsequent Get-Module checks will fail
Mock -CommandName Get-Module -ParameterFilter { ($Name -eq 'Hyper-V') -and ($ListAvailable -eq $true) }
{ Get-TargetResource -Name 'RunningVM' @testParams } | Should -Throw
{ Get-TargetResource -Name 'RunningVM' -VhdPath $stubVhdxDisk.Path } | Should -Throw
}
It 'throws when Clustering feature is not installed but VM should be clustered' {
# This test needs to be the last in the Context otherwise all subsequent Get-Module checks will fail
Mock -CommandName Get-Module -ParameterFilter { ($Name -eq 'FailoverClusters') -and ($ListAvailable -eq $true) } -MockWith { return $true }
{ Get-TargetResource -Name 'ClusteredExistingVmInClusterPresent' -VhdPath $stubVhdxDisk.Path -IsClustered $true } | Should -Throw
}
} #end context Validates Get-TargetResource Method

Expand Down Expand Up @@ -561,6 +701,18 @@ try
Test-TargetResource -Name 'VMWithAutomaticCheckpoints' -AutomaticCheckpointsEnabled $false @testParams | Should -Be $false
}

It 'Returns $true when IsClustered is enabled, Ensure is Present and VM is clustered' {
Test-TargetResource -Name ClusteredExistingVmInClusterPresent -IsClustered $true @testParams | Should -Be $true
}

It 'Returns $false when IsClustered is enabled, Ensure is Present and VM is not clustered' {
Test-TargetResource -Name ClusteredExistingVmNotInClusterPresent -IsClustered $true @testParams | Should -Be $false
}

It 'Returns $false when IsClustered is enabled, Ensure is Absent VM is present and VM is clustered' {
Test-TargetResource -Name ClusteredExistingVmInClusterAbsent -Ensure Absent -IsClustered $true @testParams | Should -Be $false
}

It 'Returns $true when EnableGuestService is on and requested "EnableGuestService" = "$true"' {
Mock -CommandName Get-VMIntegrationService -MockWith {
$guestServiceInterface = [Microsoft.HyperV.PowerShell.VMIntegrationComponent]::CreateTypeInstance()
Expand All @@ -579,6 +731,12 @@ try
{ Test-TargetResource -Name 'RunningVM' @testParams } | Should -Throw
}

It 'throws when Failover Clustering us not installed' {
# This test needs to be the last in the Context otherwise all subsequent Get-Module checks will fail
Mock -CommandName Get-Module -ParameterFilter { ($Name -eq 'FailoverClusters') -and ($ListAvailable -eq $true) }
{ Test-TargetResource -Name 'RunningVM' -IsClustered $true @testParams} | Should -Throw
}

} #end context Validates Test-TargetResource Method

Context 'Validates Set-TargetResource Method' {
Expand Down Expand Up @@ -798,6 +956,30 @@ try
Assert-MockCalled -CommandName Disable-VMIntegrationService -Exactly -Times 1 -Scope It
}

Mock -CommandName Get-ClusterGroup -MockWith {@{
Name = 'VM'
OwnerNode = 'HOST1'
State = 'Online'
}}
Mock -CommandName Remove-ClusterGroup
Mock -CommandName Add-ClusterVirtualMachineRole

It 'Calls Get- and Remove-ClusterGroup if Ensure is Absent and VM exists' {
Set-TargetResource -Name 'ClusteredExistingVmInClusterAbsent' -IsClustered $true -Ensure Absent @testParams
Assert-MockCalled -CommandName Remove-ClusterGroup -Exactly -Times 1 -Scope It
Assert-MockCalled -CommandName Get-ClusterGroup -Exactly -Times 1 -Scope It
}

It 'Calls Add-ClusterVirtualMachineRole if Ensure is Present and VM exists but is not clustered yet' {
Set-TargetResource -Name 'ClusteredExistingVmNotInClusterPresent' -IsClustered $true @testParams
Assert-MockCalled -CommandName Add-ClusterVirtualMachineRole -Exactly -Times 1 -Scope It
}

It 'Calls Add-ClusterVirtualMachineRole if Ensure is Present and VM does not exist' {
Set-TargetResource -Name 'NonexistentVm' -IsClustered $true @testParams
Assert-MockCalled -CommandName Add-ClusterVirtualMachineRole -Exactly -Times 1 -Scope It
}

Mock -CommandName Get-Command -ParameterFilter { $Name -eq 'Set-VM' -and $Module -eq 'Hyper-V' } -MockWith {
[pscustomobject]@{
parameters = @{
Expand Down Expand Up @@ -901,6 +1083,11 @@ try
Mock -CommandName Get-Module -ParameterFilter { ($Name -eq 'Hyper-V') -and ($ListAvailable -eq $true) }
{ Set-TargetResource -Name 'RunningVM' @testParams } | Should -Throw
}

It 'Does not call "Set-VM" when "AutomaticCheckpointsEnabled" is unsupported and unspecified' {
Set-TargetResource -Name 'VMAutomaticCheckpointsUnsupported' @testParams
Assert-MockCalled -CommandName Set-VM -Exactly -Times 0 -Scope It
}
} #end context Validates Set-TargetResource Method

Context 'Validates Test-VMSecureBoot Method' {
Expand Down Expand Up @@ -955,7 +1142,7 @@ try
}

} #end context validates Get-VhdHierarchy
} #end describe xVMHyper-V
} #end describe VMHyperV
} #end inmodulescope
}
finally
Expand Down

0 comments on commit 963f217

Please sign in to comment.