Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add inclusion in Hyper-V cluster #210

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ For older change log history see the [historic changelog](HISTORIC_CHANGELOG.md)
- Changed all MSFT_xResourceName to DSC_ResourceName.
- Updated DSCResources, Examples, Modules and Tests for new naming.
- Updated README.md from _xHyper-V_ to _HyperVDsc
- VMHyperV now includes VMs in cluster if host is in cluster
- Renamed default branch to `main` - Fixes [Issue #198](https://github.com/dsccommunity/HyperVDsc/issues/198).
- Moved documentation to the HyperVDsc GitHub Wiki.
- Updated all examples to correct folders and naming so they show up
Expand All @@ -22,6 +23,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
2 changes: 1 addition & 1 deletion source/DSCResources/DSC_VMDvdDrive/DSC_VMDvdDrive.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ function Test-ParameterValid
} # if

# Does the VM exist?
$null = Get-VM -Name $VMName
$null = Get-VMHyperV -VMName $VMName

# Does the controller exist?
if (-not (Get-VMScsiController -VMName $VMName -ControllerNumber $ControllerNumber) `
Expand Down
599 changes: 312 additions & 287 deletions source/DSCResources/DSC_VMHyperV/DSC_VMHyperV.psm1

Large diffs are not rendered by default.

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,6 @@ ConvertFrom-StringData @'
VMPropertySet = VM property '{0}' is '{1}'.
VMPropertiesUpdated = VM '{0}' properties have been updated.
QueryingVM = Querying VM '{0}'.
RemoveClusterGroup = Removing VM '{0}' from cluster.
AddingToCluster = Adding VM '{0}' to cluster.
'@
101 changes: 63 additions & 38 deletions source/Modules/HyperVDsc.Common/HyperVDsc.Common.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,10 @@ function Set-VMProperty
param
(
[Parameter(Mandatory = $true, ParameterSetName = 'Name')]
[Alias('VMName')]
[System.String]
$Name,

[Parameter(Mandatory = $true, ParameterSetName = 'VMName')]
[System.String]
$VMName,

[Parameter(Mandatory = $true)]
[System.String]
$VMCommand,
Expand All @@ -58,33 +55,16 @@ function Set-VMProperty
$RestartIfNeeded
)

if ($PSBoundParameters.ContainsKey('VMName'))
{
# Add the -Name property to the ChangeProperty hashtable for splatting
$ChangeProperty['VMName'] = $VMName

# Set the common parameters for splatting against Get-VM and Set-VMState
$vmCommonProperty = @{
Name = $VMName
}

# Ensure that the name parameter is set for verbose messages
$Name = $VMName
}
else
{
# Add the -Name property to the ChangeProperty hashtable for splatting
$ChangeProperty['Name'] = $Name

# Set the common parameters for splatting against Get-VM and Set-VMState
$vmCommonProperty = @{
Name = $Name
}
# Set the common parameters for splatting against Get-VM and Set-VMState
$vmCommonProperty = @{
Name = $Name
}

$vmObject = Get-VM @vmCommonProperty
$vmObject = Get-VMHyperV -VMName $Name
$vmOriginalState = $vmObject.State

$ChangeProperty['VM'] = $vmObject

if ($vmOriginalState -ne 'Off' -and $RestartIfNeeded)
{
# Turn the vm off to make changes
Expand Down Expand Up @@ -159,40 +139,42 @@ function Set-VMState
$WaitForIP
)

$vm = Get-VMHyperV -VMName $Name
$vmCurrentState = $vm.State

switch ($State)
{
'Running' {
$vmCurrentState = (Get-VM -Name $Name).State
if ($vmCurrentState -eq 'Paused')
{
# If VM is in paused state, use resume-vm to make it running
Write-Verbose -Message ($script:localizedData.ResumingVM -f $Name)
Resume-VM -Name $Name
$vm | Resume-VM
}
elseif ($vmCurrentState -eq 'Off')
{
# If VM is Off, use start-vm to make it running
Write-Verbose -Message ($script:localizedData.StartingVM -f $Name)
Start-VM -Name $Name
$vm | Start-VM
}

if ($WaitForIP)
{
Wait-VMIPAddress -Name $Name -Verbose
Wait-VMIPAddress -Name $Name
}
}
'Paused' {
if ($vmCurrentState -ne 'Off')
{
Write-Verbose -Message ($script:localizedData.SuspendingVM -f $Name)
Suspend-VM -Name $Name
$vm | Suspend-VM
}
}
'Off' {
if ($vmCurrentState -ne 'Off')
{
Write-Verbose -Message ($script:localizedData.StoppingVM -f $Name)
Stop-VM -Name $Name -Force -WarningAction SilentlyContinue
$vm | Stop-VM -Force -WarningAction SilentlyContinue
}
}
}
Expand Down Expand Up @@ -224,7 +206,8 @@ function Wait-VMIPAddress
)

[System.Int32] $elapsedSeconds = 0
while ((Get-VMNetworkAdapter -VMName $Name).IpAddresses.Count -lt 2)
$vm = Get-VMHyperV -VMName $Name
while (($vm | Get-VMNetworkAdapter).IpAddresses.Count -lt 2)
{
Write-Verbose -Message ($script:localizedData.WaitingForVMIPAddress -f $Name)
Start-Sleep -Seconds 3
Expand Down Expand Up @@ -320,22 +303,64 @@ function ConvertFrom-TimeSpan
#>
function Get-VMHyperV
{
[OutputType([System.Collections.ArrayList])]
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[System.String]
$VMName
$VMName,

[Parameter()]
[switch]
$ThrowOnEmpty
)

$vm = Get-VM -Name $VMName -ErrorAction SilentlyContinue
$vmParam = @{
ErrorAction = 'SilentlyContinue'
}

if (Get-Command -Name Get-Cluster -ErrorAction SilentlyContinue)
{
[bool] $isClustered = $null -ne (Get-Cluster -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
}

$vms = [System.Collections.ArrayList]::new()

if ($isClustered)
{
$clusterVm = Get-ClusterGroup -Name $VMName -ErrorAction SilentlyContinue

if ($clusterVm)
{
[Microsoft.HyperV.PowerShell.VirtualMachine[]]$clusteredVm = Get-VM @vmParam -ClusterObject $clusterVm
}

if ($clusteredVm)
{
$vms.AddRange($clusteredVm)
}
}

[Microsoft.HyperV.PowerShell.VirtualMachine[]]$nonClusteredVm = Get-VM @vmParam -VMName $VMName | Where-Object IsClustered -eq $false

if ($nonClusteredVm)
{
$vms.AddRange($nonClusteredVm)
}

# Check if 1 or 0 VM with name = $name exist
if ($vm.count -gt 1)
if ($vms.count -gt 1)
{
$errorMessage = $script:localizedData.MoreThanOneVMExistsError -f $VMName
New-InvalidResultException -Message $errorMessage
}

return $vm
if (-not $vms -and $ThrowOnEmpty.IsPresent)
{
$errorMessage = $script:localizedData.NoVMExistsError -f $VMName
New-InvalidResultException -Message $errorMessage
}

return $vms
} #end function Get-VMHyperV
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ ConvertFrom-StringData @'
CannotUpdatePropertiesOnlineError = Can not change properties for VM '{0}' in '{1}' state unless 'RestartIfNeeded' is set to true.
WaitForVMIPAddressTimeoutError = Waiting for VM '{0}' IP address timed out after {1} seconds.
MoreThanOneVMExistsError = More than one VM with the name '{0}' exists.
NoVMExistsError = No VM with the name '{0}' exists.
'@
22 changes: 11 additions & 11 deletions tests/Unit/DSC_VMDvdDrive.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ try
-Verifiable

# Mocks that should not be called
Mock -CommandName Get-VM
Mock -CommandName Get-VMHyperV
Mock -CommandName Get-VMScsiController
Mock -CommandName Get-VMIdeController
Mock -CommandName Get-VMHardDiskDrive
Expand All @@ -473,7 +473,7 @@ try
-Verifiable

Mock `
-CommandName Get-VM `
-CommandName Get-VMHyperV `
-MockWith { throw } `
-ParameterFilter { $VMName -eq $script:splatAddDvdDriveNoPath.VMName } `
-Verifiable
Expand All @@ -490,7 +490,7 @@ try
It 'all the get mocks should be called' {
Assert-VerifiableMock
Assert-MockCalled -CommandName Get-Module -Exactly 1
Assert-MockCalled -CommandName Get-VM -Exactly 1 `
Assert-MockCalled -CommandName Get-VMHyperV -Exactly 1 `
-ParameterFilter { $VMName -eq $script:splatAddDvdDriveNoPath.VMName }
}
}
Expand All @@ -503,7 +503,7 @@ try
-Verifiable

Mock `
-CommandName Get-VM `
-CommandName Get-VMHyperV `
-MockWith { $script:mockGetVM } `
-ParameterFilter { $VMName -eq $script:splatAddDvdDriveNoPath.VMName } `
-Verifiable
Expand All @@ -530,7 +530,7 @@ try
It 'all the get mocks should be called' {
Assert-VerifiableMock
Assert-MockCalled -CommandName Get-Module -Exactly 1
Assert-MockCalled -CommandName Get-VM -Exactly 1 `
Assert-MockCalled -CommandName Get-VMHyperV -Exactly 1 `
-ParameterFilter { $VMName -eq $script:splatAddDvdDriveNoPath.VMName }
Assert-MockCalled -CommandName Get-VMScsiController -Exactly 1 `
-ParameterFilter { $VMName -eq $script:splatAddDvdDriveNoPath.VMName }
Expand All @@ -547,7 +547,7 @@ try
-Verifiable

Mock `
-CommandName Get-VM `
-CommandName Get-VMHyperV `
-MockWith { $script:mockGetVM } `
-ParameterFilter { $VMName -eq $script:splatAddDvdDriveNoPath.VMName } `
-Verifiable
Expand Down Expand Up @@ -576,7 +576,7 @@ try
It 'all the get mocks should be called' {
Assert-VerifiableMock
Assert-MockCalled -CommandName Get-Module -Exactly 1
Assert-MockCalled -CommandName Get-VM -Exactly 1 `
Assert-MockCalled -CommandName Get-VMHyperV -Exactly 1 `
-ParameterFilter { $VMName -eq $script:splatAddDvdDriveNoPath.VMName }
Assert-MockCalled -CommandName Get-VMScsiController -Exactly 1 `
-ParameterFilter { $VMName -eq $script:splatAddDvdDriveNoPath.VMName }
Expand All @@ -593,7 +593,7 @@ try
-Verifiable

Mock `
-CommandName Get-VM `
-CommandName Get-VMHyperV `
-MockWith { $script:mockGetVM } `
-ParameterFilter { $VMName -eq $script:splatAddDvdDrive.VMName } `
-Verifiable
Expand Down Expand Up @@ -626,7 +626,7 @@ try
It 'all the get mocks should be called' {
Assert-VerifiableMock
Assert-MockCalled -CommandName Get-Module -Exactly 1
Assert-MockCalled -CommandName Get-VM -Exactly 1 `
Assert-MockCalled -CommandName Get-VMHyperV -Exactly 1 `
-ParameterFilter { $VMName -eq $script:splatAddDvdDrive.VMName }
Assert-MockCalled -CommandName Get-VMScsiController -Exactly 1 `
-ParameterFilter { $VMName -eq $script:splatAddDvdDrive.VMName }
Expand All @@ -644,7 +644,7 @@ try
-Verifiable

Mock `
-CommandName Get-VM `
-CommandName Get-VMHyperV `
-MockWith { $script:mockGetVM } `
-ParameterFilter { $VMName -eq $script:splatAddDvdDrive.VMName } `
-Verifiable
Expand Down Expand Up @@ -675,7 +675,7 @@ try
It 'all the get mocks should be called' {
Assert-VerifiableMock
Assert-MockCalled -CommandName Get-Module -Exactly 1
Assert-MockCalled -CommandName Get-VM -Exactly 1 `
Assert-MockCalled -CommandName Get-VMHyperV -Exactly 1 `
-ParameterFilter { $VMName -eq $script:splatAddDvdDrive.VMName }
Assert-MockCalled -CommandName Get-VMScsiController -Exactly 1 `
-ParameterFilter { $VMName -eq $script:splatAddDvdDrive.VMName }
Expand Down
Loading