Skip to content

Commit 84d7d50

Browse files
Merge branch 'hvvm' into 'dev'
Introduces support for Hyper-V hosts w/o SCVMM server See merge request fozzy-winadmins/AutomaticMaintenance!7
2 parents 3f34448 + 453b895 commit 84d7d50

11 files changed

Lines changed: 256 additions & 25 deletions

AutomaticMaintenance-Hosts-Example.json

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@
1010
},
1111
{
1212
"Name": "SRV03",
13-
"Template": "Example-Template2"
13+
"Template": "Example-SCVMM"
1414
},
1515
{
1616
"Name": "SRV04",
17-
"Template": "Example-Template2",
17+
"Template": "Example-SCVMM",
1818
"MyCustomAttribute": "CustomValue1",
1919
"PostRestoreCommands": "",
2020
"Workload": [
@@ -45,5 +45,31 @@
4545
"Filter": "$_.Name -notlike '*-DontMove'"
4646
}
4747
]
48+
},
49+
{
50+
"Name": "SRV06",
51+
"Template": "Example-Hyper-V",
52+
"PutInASubfolder": false,
53+
"Workload": [
54+
{
55+
"Path": "D:\\VMs",
56+
"DestinationName": "SRV07",
57+
"DestinationPath": "D:\\VMs",
58+
"Filter": "$_.Name -like '*'"
59+
}
60+
]
61+
},
62+
{
63+
"Name": "SRV07",
64+
"Template": "Example-Hyper-V",
65+
"Workload": [
66+
{
67+
"Path": "D:\\VMs",
68+
"DestinationName": "SRV06",
69+
"DestinationPath": "D:\\VMs",
70+
"Filter": "$_.Name -like '*'",
71+
"PutInASubfolder": true
72+
}
73+
]
4874
}
4975
]

AutomaticMaintenance-Templates-Example.json

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
}
1313
},
1414
{
15-
"Name": "Example-Template2",
15+
"Name": "Example-SCVMM",
1616
"Include": [
1717
{
1818
"Priority": 1,
@@ -37,5 +37,30 @@
3737
}
3838
]
3939
}
40+
},
41+
{
42+
"Name": "Hosts-With-Silverlight",
43+
"Properties": {
44+
"UpdateInstallFilter": "$_.Title -notlike '*Preview*' -and $_.Title -notlike '*Security Only*'"
45+
}
46+
},
47+
{
48+
"Name": "Example-Hyper-V",
49+
"Include": [
50+
{
51+
"Priority": 1,
52+
"Name": "Example-Template"
53+
},
54+
{
55+
"Priority": 2,
56+
"Name": "Hosts-With-Silverlight"
57+
}
58+
],
59+
"Properties": {
60+
"Type": "HV-Vanilla",
61+
"PutInASubfolder": true,
62+
"TestCommands": "Example-Test-HV.ps1",
63+
"FinallyCommands": ""
64+
}
4065
}
4166
]

AutomaticMaintenance.psm1

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ $ModuleName = ($MyInvocation.MyCommand.Name).Substring(0, ($MyInvocation.MyComma
3838
[string]$ModuleWideInstallUpdateDefaultFilterString = '$_ -like ''*'''
3939
[string]$ModuleWideUpdateSearchCriteria = 'IsInstalled=0 and IsHidden=0'
4040

41+
[bool]$ModuleWideHVVanillaPutInASubfolder = $true
42+
4143
foreach ($FunctionType in @('Private', 'Public')) {
4244
$Path = Join-Path -Path $ModulePath -ChildPath ('{0}\*.ps1' -f $FunctionType)
4345
if (Test-Path -Path $Path) {

Config-Example.ps1

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,6 @@
3535
[string]$ModuleWideInstallUpdateTaskDescription = 'Automatic Maintenance Windows Update Installer Task'
3636
[string]$ModuleWideCheckUpdateDefaultFilterString = '$_.Title -notlike ''Definition Update for Windows Defender Antivirus *''' # Defender updates are released constantly and do not require reboot. If we would not exclude them, there would be constant useless workload movement.
3737
[string]$ModuleWideInstallUpdateDefaultFilterString = '$_ -like ''*'''
38-
[string]$ModuleWideUpdateSearchCriteria = 'IsInstalled=0 and IsHidden=0'
38+
[string]$ModuleWideUpdateSearchCriteria = 'IsInstalled=0 and IsHidden=0'
39+
40+
[bool]$ModuleWideHVVanillaPutInASubfolder = $true
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
function Clear-ComputerWorkloadHVVanilla {
2+
#Requires -Version 3.0
3+
#Requires -Modules ResourceLocker
4+
5+
[CmdletBinding()]
6+
[OutputType([System.Boolean])]
7+
Param (
8+
[Parameter(Mandatory)]
9+
[string]$ComputerName,
10+
[Parameter(Mandatory)]
11+
[string]$DestinationVMHostName,
12+
[Parameter(Mandatory)]
13+
[string]$DestinationVMHostPath,
14+
[scriptblock]$SourceFilter,
15+
[scriptblock]$DestinationFilter,
16+
[int]$MaxParallelMigrations,
17+
[switch]$PutInASubfolder = $ModuleWideHVVanillaPutInASubfolder,
18+
[ref]$DestinationVMHostLock
19+
)
20+
21+
$ErrorActionPreference = 'Stop'
22+
23+
Write-Debug -Message ('ENTER {0}' -f $MyInvocation.MyCommand.Name)
24+
25+
try {
26+
Write-Debug -Message ('ENTER TRY {0}' -f $MyInvocation.MyCommand.Name)
27+
28+
Write-Debug -Message ('$ComputerName = ''{0}''' -f $ComputerName)
29+
Write-Debug -Message ('$DestinationVMHostName = ''{0}''' -f $DestinationVMHostName)
30+
Write-Debug -Message ('$DestinationVMHostPath = ''{0}''' -f $DestinationVMHostPath)
31+
Write-Debug -Message ('$SourceFilter = {{{0}}}' -f $SourceFilter)
32+
Write-Debug -Message ('$DestinationFilter = {{{0}}}' -f $DestinationFilter)
33+
Write-Debug -Message ('$MaxParallelMigrations = {0}' -f $MaxParallelMigrations)
34+
Write-Debug -Message ('$PutInASubfolder = ${0}' -f $PutInASubfolder)
35+
Write-Debug -Message ('$DestinationVMHostLock: ''{0}''' -f $DestinationVMHostLock)
36+
Write-Debug -Message ('$DestinationVMHostLock.Value: ''{0}''' -f $DestinationVMHostLock.Value)
37+
38+
Write-Debug -Message '$CallerName = Get-LockCallerName'
39+
$CallerName = Get-LockCallerName
40+
Write-Debug -Message ('$CallerName = ''{0}''' -f $CallerName)
41+
42+
Write-Debug -Message ('$AllSourceVMs = Get-VM -ComputerName ''{0}''' -f $ComputerName)
43+
$AllSourceVMs = Get-VM -ComputerName $ComputerName
44+
Write-Debug -Message ('$AllSourceVMs: ''{0}''' -f [string]$AllSourceVMs.Name)
45+
46+
Write-Debug -Message 'if ($SourceFilter)'
47+
if ($SourceFilter) {
48+
Write-Debug -Message ('$SourceVMs = $AllSourceVMs | Where-Object -FilterScript {{{0}}}' -f $SourceFilter)
49+
$SourceVMs = $AllSourceVMs | Where-Object -FilterScript $SourceFilter
50+
}
51+
else {
52+
Write-Debug -Message '$SourceVMs = $AllSourceVMs'
53+
$SourceVMs = $AllSourceVMs
54+
}
55+
Write-Debug -Message ('$SourceVMs: ''{0}''' -f [string]$SourceVMs.Name)
56+
57+
Write-Debug -Message 'if ($SourceVMs)'
58+
if ($SourceVMs) {
59+
Write-Debug -Message ('$AllDestinationVMs = Get-VM -ComputerName ''{0}''' -f $DestinationVMHostName)
60+
$AllDestinationVMs = Get-VM -ComputerName $DestinationVMHostName
61+
Write-Debug -Message ('$AllDestinationVMs: ''{0}''' -f [string]$AllDestinationVMs.Name)
62+
63+
Write-Debug -Message ('$DestinationFilter = {{{0}}}' -f $DestinationFilter)
64+
Write-Debug -Message 'if ($DestinationFilter)'
65+
if ($DestinationFilter) {
66+
Write-Debug -Message ('$DestinationVMs = $AllDestinationVMs | Where-Object -FilterScript {{{0}}}' -f $DestinationFilter)
67+
$DestinationVMs = $AllDestinationVMs | Where-Object -FilterScript $DestinationFilter
68+
}
69+
else {
70+
Write-Debug -Message '$DestinationVMs = $AllDestinationVMs'
71+
$DestinationVMs = $AllDestinationVMs
72+
}
73+
Write-Debug -Message ('$DestinationVMs: ''{0}''' -f [string]$DestinationVMs.Name)
74+
75+
Write-Debug -Message 'if ($DestinationVMs)'
76+
if ($DestinationVMs) {
77+
$Message = 'Unable to proceed: the destination server {0} should not contain any VMs, but it does ({1})' -f $DestinationVMHostName, [string]$DestinationVMs.Name
78+
$PSCmdlet.ThrowTerminatingError((New-Object -TypeName 'System.Management.Automation.ErrorRecord' -ArgumentList ((New-Object -TypeName 'System.InvalidOperationException' -ArgumentList $Message), 'InvalidOperationException', [System.Management.Automation.ErrorCategory]::LimitsExceeded, $null)))
79+
}
80+
81+
Write-Debug -Message ('$DestinationVMHostLock.Value: ''{0}''' -f [string]$DestinationVMHostLock.Value)
82+
Write-Debug -Message 'if (-not ($DestinationVMHostLock.Value))'
83+
if (-not ($DestinationVMHostLock.Value)) {
84+
Write-Debug -Message ('$DestinationVMHostLock.Value = Lock-HostResource -ComputerName {0} -CallerName {1}' -f $DestinationVMHostName, $CallerName)
85+
$DestinationVMHostLock.Value = Lock-HostResource -ComputerName $DestinationVMHostName -CallerName $CallerName
86+
Write-Debug -Message ('$DestinationHostLock: ''{0}''' -f $DestinationHostLock)
87+
Write-Debug -Message ('$DestinationHostLock.Value: ''{0}''' -f $DestinationHostLock.Value)
88+
}
89+
Write-Debug -Message ('$SourceVMs: ''{0}''' -f [string]$SourceVMs.Name)
90+
91+
Write-Debug -Message ('$DestinationVMHost = Get-VMHost -ComputerName ''{0}''' -f $DestinationVMHostName)
92+
$DestinationVMHost = Get-VMHost -ComputerName $DestinationVMHostName
93+
Write-Debug -Message ('$DestinationVMHost: ''{0}''' -f $DestinationVMHost)
94+
Write-Debug -Message ('$UnmigratableVMs = Move-VMReliably -DestinationVMHost $DestinationVMHost -Path ''{0}'' -VM $SourceVMs -MaxParallelMigrations {1} -PutInASubfolder:${2}' -f $DestinationVMHostPath, $MaxParallelMigrations, $PutInASubfolder)
95+
$UnmigratableVMs = Move-VMReliably -DestinationVMHost $DestinationVMHost -Path $DestinationVMHostPath -VM $SourceVMs -MaxParallelMigrations $MaxParallelMigrations -PutInASubfolder:$PutInASubfolder
96+
Write-Debug -Message ('$UnmigratableVMs: ''{0}''' -f [string]$UnmigratableVMs.Name)
97+
Write-Debug -Message 'if ($UnmigratableVMs)'
98+
if ($UnmigratableVMs) {
99+
$Message = 'Unable to proceed: unable to migrate some VMs ({0}) from host {1} to host {2}' -f [string]$UnmigratableVMs.Name, $ComputerName, $DestinationVMHostName
100+
$PSCmdlet.ThrowTerminatingError((New-Object -TypeName 'System.Management.Automation.ErrorRecord' -ArgumentList ((New-Object -TypeName 'System.InvalidOperationException' -ArgumentList $Message), 'InvalidOperationException', [System.Management.Automation.ErrorCategory]::ResourceBusy, $null)))
101+
}
102+
103+
Write-Debug -Message '$true'
104+
$true
105+
}
106+
else {
107+
Write-Debug -Message '$false'
108+
$false
109+
}
110+
111+
Write-Debug -Message ('EXIT TRY {0}' -f $MyInvocation.MyCommand.Name)
112+
}
113+
catch {
114+
Write-Debug -Message ('ENTER CATCH {0}' -f $MyInvocation.MyCommand.Name)
115+
116+
Write-Debug -Message ('{0}: $PSCmdlet.ThrowTerminatingError($_)' -f $MyInvocation.MyCommand.Name)
117+
$PSCmdlet.ThrowTerminatingError($_)
118+
119+
Write-Debug -Message ('EXIT CATCH {0}' -f $MyInvocation.MyCommand.Name)
120+
}
121+
122+
Write-Debug -Message ('EXIT {0}' -f $MyInvocation.MyCommand.Name)
123+
}

Private/HV/Get-HVWorkloadFilter.ps1

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ function Get-HVWorkloadFilter {
1212
[Parameter(ParameterSetName = 'ByWLPair')]
1313
[Parameter(ParameterSetName = 'ByNamePath')]
1414
[switch]$Restore,
15+
[Parameter(ParameterSetName = 'ByWLPair')]
16+
[Parameter(ParameterSetName = 'ByNamePath')]
1517
[string]$Mode
1618
)
1719

@@ -26,6 +28,7 @@ function Get-HVWorkloadFilter {
2628
Write-Debug -Message ('$ComputerName = ''{0}''' -f $ComputerName)
2729
Write-Debug -Message ('$Path = ''{0}''' -f $Path)
2830
Write-Debug -Message ('$Restore: ''{0}''' -f $Restore)
31+
Write-Debug -Message ('$Mode = ''{0}''' -f $Mode)
2932

3033
Write-Debug -Message 'if (-not $WorkloadPair)'
3134
if (-not $WorkloadPair) {

Private/Workload/Clear-ComputerWorkload.ps1

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,47 @@ function Clear-ComputerWorkload {
4545
}
4646
}
4747
'HV-Vanilla' {
48-
# TODO
48+
foreach ($WorkloadPair in $ComputerMaintenanceConfiguration.Workload) {
49+
Write-Debug -Message ('$WorkloadPair: ''{0}''' -f [string]$WorkloadPair)
50+
51+
Write-Debug -Message ('$FilterData = Get-HVWorkloadFilter -WorkloadPair $WorkloadPair -Mode ''{0}''' -f $_)
52+
$FilterData = Get-HVWorkloadFilter -WorkloadPair $WorkloadPair -Mode $_
53+
Write-Debug -Message ('$FilterData = ''{0}''' -f $FilterData)
54+
Write-Debug -Message '$SourceFilter = $FilterData.Source'
55+
$SourceFilter = $FilterData.Source
56+
Write-Debug -Message ('$SourceFilter = {{{0}}}' -f $SourceFilter)
57+
Write-Debug -Message '$DestinationFilter = $FilterData.Destination'
58+
$DestinationFilter = $FilterData.Destination
59+
Write-Debug -Message ('$DestinationFilter = {{{0}}}' -f $DestinationFilter)
60+
61+
Write-Debug -Message '$PutInASubfolderExists = Get-Member -InputObject $WorkloadPair -Name ''PutInASubfolder'''
62+
$PutInASubfolderAttribute = Get-Member -InputObject $WorkloadPair -Name 'PutInASubfolder'
63+
Write-Debug -Message ('$PutInASubfolderAttribute: ''{0}''' -f $PutInASubfolderAttribute)
64+
Write-Debug -Message 'if ($PutInASubfolderAttribute)'
65+
if ($PutInASubfolderAttribute) {
66+
Write-Debug -Message '$ClearComputerWorkloadHVVanillaParameters = @{PutInASubfolder = $WorkloadPair.PutInASubfolder}'
67+
$ClearComputerWorkloadHVVanillaParameters = @{
68+
PutInASubfolder = $WorkloadPair.PutInASubfolder
69+
}
70+
}
71+
else {
72+
Write-Debug -Message '$PutInASubfolderAttribute = Get-Member -InputObject $ComputerMaintenanceConfiguration -Name ''PutInASubfolder'''
73+
$PutInASubfolderAttribute = Get-Member -InputObject $ComputerMaintenanceConfiguration -Name 'PutInASubfolder'
74+
Write-Debug -Message 'if ($PutInASubfolderAttribute)'
75+
if ($PutInASubfolderAttribute) {
76+
Write-Debug -Message '$ClearComputerWorkloadHVVanillaParameters = @{PutInASubfolder = $ComputerMaintenanceConfiguration.PutInASubfolder}'
77+
$ClearComputerWorkloadHVVanillaParameters = @{
78+
PutInASubfolder = $ComputerMaintenanceConfiguration.PutInASubfolder
79+
}
80+
}
81+
}
82+
Write-Debug -Message ('$ClearComputerWorkloadHVVanillaParameters: ''{0}''' -f ($ClearComputerWorkloadHVVanillaParameters | Out-String))
83+
84+
Write-Debug -Message ('$DestinationHostLock: ''{0}''' -f $DestinationHostLock)
85+
Write-Debug -Message ('$DestinationHostLock.Value: ''{0}''' -f $DestinationHostLock.Value)
86+
Write-Debug -Message ('Clear-ComputerWorkloadHVVanilla -ComputerName ''{0}'' -DestinationVMHostName ''{1}'' -DestinationVMHostPath ''{2}'' -DestinationVMHostLock $DestinationHostLock -SourceFilter {{{3}}} -DestinationFilter {{{4}}} -MaxParallelMigrations {5} @ClearComputerWorkloadHVVanillaParameters' -f $ComputerName, $WorkloadPair.DestinationName, $WorkloadPair.DestinationPath, $SourceFilter, $DestinationFilter, $WorkloadPair.MaxParallelMigrations)
87+
Clear-ComputerWorkloadHVVanilla -ComputerName $ComputerName -DestinationVMHostName $WorkloadPair.DestinationName -DestinationVMHostPath $WorkloadPair.DestinationPath -DestinationVMHostLock $DestinationHostLock -SourceFilter $SourceFilter -DestinationFilter $DestinationFilter -MaxParallelMigrations $WorkloadPair.MaxParallelMigrations @ClearComputerWorkloadHVVanillaParameters
88+
}
4989
}
5090
'SCDPM' {
5191
# TODO

0 commit comments

Comments
 (0)