Categories
Sponsors
Archive
Blogroll
Badges
Community
|
Posted in Windows Server | No Comment | 965 views | 13/06/2014 22:13
This is an example script to check DNS servers on remote servers. If DNS count is not equal to 4, that will add server name into output.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| # DNS Server Control
$ServerList = Get-Content VMs.txt
foreach ($Server in $ServerList)
{
Write-Host $Server
$DNSServers = $Null
$DNSServers = (Get-WmiObject -ComputerName $Server -Class Win32_NetworkAdapterConfiguration | where {$_.DNSServerSearchOrder -ne $Null}).DNSServerSearchOrder
if ($DNSServers.Count -ne "4")
{
Add-Content -Value $Server -Path DNSProblem.txt
}
Write-Host $DNSServers
Write-Host " "
Write-Host " "
} |
# DNS Server Control
$ServerList = Get-Content VMs.txt
foreach ($Server in $ServerList)
{
Write-Host $Server
$DNSServers = $Null
$DNSServers = (Get-WmiObject -ComputerName $Server -Class Win32_NetworkAdapterConfiguration | where {$_.DNSServerSearchOrder -ne $Null}).DNSServerSearchOrder
if ($DNSServers.Count -ne "4")
{
Add-Content -Value $Server -Path DNSProblem.txt
}
Write-Host $DNSServers
Write-Host " "
Write-Host " "
}
You should check DNSProblem.txt file to see which servers don’t have 4 DNS entries.
Posted in Linux Server, Windows Powershell, Windows Server | No Comment | 2,327 views | 12/06/2014 22:44
This is an example for nxService of PowerShell DSC.
You will able to stop a service like postfix on Linux by using PowerShell DSC.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| $cred=Get-Credential -UserName:"root" -Message:"Root User?"
$opt = New-CimSessionOption -UseSsl:$true -SkipCACheck:$true -SkipCNCheck:$true -SkipRevocationCheck:$true
$linuxcomp=New-CimSession -Credential:$cred -ComputerName:linuxdsc.cloudapp.net -Port:5986 -Authentication:basic -SessionOption:$opt
Configuration StopService
{
Import-DSCResource -Module nx
Node "linuxdsc.cloudapp.net"{
nxService StopService
{
Name = "postfix"
Controller = "init"
State = "Stopped"
}
}
}
StopService -OutputPath:"C:\temp"
Start-DscConfiguration -CimSession:$linuxcomp -Path:"C:\temp" -Verbose -Wait |
$cred=Get-Credential -UserName:"root" -Message:"Root User?"
$opt = New-CimSessionOption -UseSsl:$true -SkipCACheck:$true -SkipCNCheck:$true -SkipRevocationCheck:$true
$linuxcomp=New-CimSession -Credential:$cred -ComputerName:linuxdsc.cloudapp.net -Port:5986 -Authentication:basic -SessionOption:$opt
Configuration StopService
{
Import-DSCResource -Module nx
Node "linuxdsc.cloudapp.net"{
nxService StopService
{
Name = "postfix"
Controller = "init"
State = "Stopped"
}
}
}
StopService -OutputPath:"C:\temp"
Start-DscConfiguration -CimSession:$linuxcomp -Path:"C:\temp" -Verbose -Wait
After you run DSC, postfix service will be stopped on destination Linux Server.
Posted in Virtual Machine Manager, Windows Server | No Comment | 1,968 views | 09/06/2014 17:54
This is my notes and scripts for a Hyper-V Disaster Recovery with Storage Replication.
First script on Main Site, creates Volume identifiers under CSV Volumes. So after a Volume restore, we can restore Volume names with that index. Also scripts exports all virtual machine configs. That will make it easier to restore all Virtual Machines on Hyper-V.
On Main Site:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
| ##################################################
# START ON MAIN SITE
##################################################
# Clear VM Config
$TestVMConfigPath = Test-Path -Path "C:\ClusterStorage\Volume1\VMExport.Config"
if ($TestVMConfigPath)
{
Clear-Content -Path "C:\ClusterStorage\Volume1\VMExport.Config"
}
# Export VM Config
$ClusterNodes = Get-Cluster | Get-ClusterNode
foreach ($ClusterNode in $ClusterNodes)
{
# Get Virtual Machines
$VMs = Get-VM -ComputerName $ClusterNode
# Export VM Config
foreach ($VM in $VMs)
{
# Get Virtual Machines
$VMConfigPath = $VM.ConfigurationLocation + "\Virtual Machines\" + $VM.Id + ".xml"
# Export to Config File
Add-Content -Value $VMConfigPath -Path "C:\ClusterStorage\Volume1\VMExport.Config"
}
}
# Read All CSV Directories
$CSVVolumes = Get-ChildItem "C:\ClusterStorage"
foreach ($CSVVolume in $CSVVolumes)
{
# Get CSV Volume Name
$CSVVolumeName = $CSVVolume.Name
# Get CSV Identifier Name
$CSVIdentifierName = $CSVVolumeName + ".CSV"
# Get CSV Path
$CSVPath = $CSVVolume.FullName
# Get CSV Identifier Path
$CSVIdentifierPath = $CSVPath + "\" + $CSVIdentifierName
# Test CSV Identifier Path
$TestCSVIdentifierPath = Test-Path $CSVIdentifierPath
if (!$TestCSVIdentifierPath)
{
# Create CSV Identifier
New-Item -Path "$CSVPath" -Name "$CSVIdentifierName" -ItemType File
}
} |
##################################################
# START ON MAIN SITE
##################################################
# Clear VM Config
$TestVMConfigPath = Test-Path -Path "C:\ClusterStorage\Volume1\VMExport.Config"
if ($TestVMConfigPath)
{
Clear-Content -Path "C:\ClusterStorage\Volume1\VMExport.Config"
}
# Export VM Config
$ClusterNodes = Get-Cluster | Get-ClusterNode
foreach ($ClusterNode in $ClusterNodes)
{
# Get Virtual Machines
$VMs = Get-VM -ComputerName $ClusterNode
# Export VM Config
foreach ($VM in $VMs)
{
# Get Virtual Machines
$VMConfigPath = $VM.ConfigurationLocation + "\Virtual Machines\" + $VM.Id + ".xml"
# Export to Config File
Add-Content -Value $VMConfigPath -Path "C:\ClusterStorage\Volume1\VMExport.Config"
}
}
# Read All CSV Directories
$CSVVolumes = Get-ChildItem "C:\ClusterStorage"
foreach ($CSVVolume in $CSVVolumes)
{
# Get CSV Volume Name
$CSVVolumeName = $CSVVolume.Name
# Get CSV Identifier Name
$CSVIdentifierName = $CSVVolumeName + ".CSV"
# Get CSV Path
$CSVPath = $CSVVolume.FullName
# Get CSV Identifier Path
$CSVIdentifierPath = $CSVPath + "\" + $CSVIdentifierName
# Test CSV Identifier Path
$TestCSVIdentifierPath = Test-Path $CSVIdentifierPath
if (!$TestCSVIdentifierPath)
{
# Create CSV Identifier
New-Item -Path "$CSVPath" -Name "$CSVIdentifierName" -ItemType File
}
}
Second Script on DR site, clears all old objects on Hyper-V and Failover Cluster. Restores volume names according to Volume Identifiers. After that imports all virtual machines by using our virtual machine exports.
On DR Site:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
| ##################################################
# START ON DR SITE
##################################################
# Clear Failover Cluster Objects
$ClusterGroup = Get-ClusterGroup | Where GroupType -eq "VirtualMachine" | Remove-ClusterGroup -RemoveResources -Force
# Get All Cluster Nodes
$ClusterNodes = Get-Cluster | Get-ClusterNode
if ($ClusterNode in $ClusterNodes)
{
Invoke-Command -ComputerName $ClusterNode -ScriptBlock {
# Stop Hyper-V Management Service
Set-Service "vmms" -StartupType Disabled
Stop-Service "vmms"
# Clear Hyper-V Database
Get-ChildItem -Path "C:\ProgramData\Microsoft\Windows\Hyper-V\Virtual Machines" | Remove-Item
}
}
# Read All CSV Directories
$CSVVolumes = Get-ChildItem "C:\ClusterStorage"
# Clear Iteration
$i = 1;
foreach ($CSVVolume in $CSVVolumes)
{
# Create Time Guid
$TimeGuid = (Get-Date).ToString("ddMMyyyyhhmmss")
# Change CSV Volume Name
$CSVVolume | Rename-Item -NewName "CSVolume$TimeGuid$i"
# Update Iteration
$i++
}
# Read Updated CSV Directories
$CSVVolumes = Get-ChildItem "C:\ClusterStorage"
foreach ($CSVVolume in $CSVVolumes)
{
# Get CSV Volume Name
$CSVVolumeName = $CSVVolume.Name
# Get CSV Volume Filter
$CSVVolumeFilter = "*.CSV"
# Get CSV Path
$CSVPath = $CSVVolume.FullName
# Find CSV Identifier
$CSVIdentifier = $Null;
$CSVIdentifier = (Get-ChildItem "$CSVPath" -Filter "$CSVVolumeFilter" -Recurse).Name
$CSVIdentifier = $CSVIdentifier.Split(".")[0]
if ($CSVIdentifier)
{
# CSV Volume ID
$NewCSVVolumeName = "Volume" + $CSVIdentifier.Substring("0,6")
# Change CSV Volume Name
$CSVVolume | Rename-Item -NewName "$NewCSVVolumeName"
}
else
{
Write-Host "Error: Could not find CSV Identifier for $CSVVolume"
}
}
# Get All Cluster Nodes
$ClusterNodes = Get-Cluster | Get-ClusterNode
if ($ClusterNode in $ClusterNodes)
{
Invoke-Command -ComputerName $ClusterNode -ScriptBlock {
# Start Hyper-V Management Service
Set-Service "vmms" -StartupType Automatic
Start-Service "vmms"
}
}
# Get VM Import Config
$VMImportConfig = Get-Content "C:\ClusterStorage\Volume1\VMExport.Config"
foreach ($VM in $VMImportConfig)
{
# Import Virtual Machine
$ImportVM = Import-VM $VM
# Get Virtual Machine Name
$VMName = $ImportVM.Name
# Add VM to Failover Cluster
Add-VMToCluster $VMName
} |
##################################################
# START ON DR SITE
##################################################
# Clear Failover Cluster Objects
$ClusterGroup = Get-ClusterGroup | Where GroupType -eq "VirtualMachine" | Remove-ClusterGroup -RemoveResources -Force
# Get All Cluster Nodes
$ClusterNodes = Get-Cluster | Get-ClusterNode
if ($ClusterNode in $ClusterNodes)
{
Invoke-Command -ComputerName $ClusterNode -ScriptBlock {
# Stop Hyper-V Management Service
Set-Service "vmms" -StartupType Disabled
Stop-Service "vmms"
# Clear Hyper-V Database
Get-ChildItem -Path "C:\ProgramData\Microsoft\Windows\Hyper-V\Virtual Machines" | Remove-Item
}
}
# Read All CSV Directories
$CSVVolumes = Get-ChildItem "C:\ClusterStorage"
# Clear Iteration
$i = 1;
foreach ($CSVVolume in $CSVVolumes)
{
# Create Time Guid
$TimeGuid = (Get-Date).ToString("ddMMyyyyhhmmss")
# Change CSV Volume Name
$CSVVolume | Rename-Item -NewName "CSVolume$TimeGuid$i"
# Update Iteration
$i++
}
# Read Updated CSV Directories
$CSVVolumes = Get-ChildItem "C:\ClusterStorage"
foreach ($CSVVolume in $CSVVolumes)
{
# Get CSV Volume Name
$CSVVolumeName = $CSVVolume.Name
# Get CSV Volume Filter
$CSVVolumeFilter = "*.CSV"
# Get CSV Path
$CSVPath = $CSVVolume.FullName
# Find CSV Identifier
$CSVIdentifier = $Null;
$CSVIdentifier = (Get-ChildItem "$CSVPath" -Filter "$CSVVolumeFilter" -Recurse).Name
$CSVIdentifier = $CSVIdentifier.Split(".")[0]
if ($CSVIdentifier)
{
# CSV Volume ID
$NewCSVVolumeName = "Volume" + $CSVIdentifier.Substring("0,6")
# Change CSV Volume Name
$CSVVolume | Rename-Item -NewName "$NewCSVVolumeName"
}
else
{
Write-Host "Error: Could not find CSV Identifier for $CSVVolume"
}
}
# Get All Cluster Nodes
$ClusterNodes = Get-Cluster | Get-ClusterNode
if ($ClusterNode in $ClusterNodes)
{
Invoke-Command -ComputerName $ClusterNode -ScriptBlock {
# Start Hyper-V Management Service
Set-Service "vmms" -StartupType Automatic
Start-Service "vmms"
}
}
# Get VM Import Config
$VMImportConfig = Get-Content "C:\ClusterStorage\Volume1\VMExport.Config"
foreach ($VM in $VMImportConfig)
{
# Import Virtual Machine
$ImportVM = Import-VM $VM
# Get Virtual Machine Name
$VMName = $ImportVM.Name
# Add VM to Failover Cluster
Add-VMToCluster $VMName
}
After that, you will be able to start your virtual machines.
Posted in Virtual Machine Manager, Windows Powershell, Windows Server | 3 Comments | 3,062 views | 09/06/2014 01:19
Sometimes, you may need to empty your CSV volume for a maintenance operation or due to a volume corruption.
So you can use this script if you need to drain all virtual machines and virtual disks from a CSV.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| # Target Volume
$TargetVolume = "Volume8"
# Get Cluster Nodes
$ClusterNodes = Get-Cluster | Get-ClusterNode
foreach ($ClusterNode in $ClusterNodes)
{
# Clear Variables
$VMs = $Null;
# Get All Virtual Machines on Target Volume
$VMs = Get-VM -ComputerName "$ClusterNode" | Where ConfigurationLocation -like "C:\ClusterStorage\$TargetVolume*"
foreach ($VM in $VMs)
{
# Get VM Information
$VMName = $VM.Name
Write-Host " "
Write-Host "Working on $VMName .."
Write-Host "Hyper-V Host: $ClusterNode"
# Get Volume Information
$Volume = ((Get-ClusterSharedVolume | Where {$_.SharedVolumeInfo.FriendlyVolumeName -notlike "*$TargetVolume*"} | Select -ExpandProperty SharedVolumeInfo | Select @{label="Name";expression={(($_.FriendlyVolumeName).Split("\"))[-1]}},@{label="FreeSpace";expression={($_ | Select -Expand Partition).FreeSpace}} | Sort FreeSpace -Descending)[0]).Name
# Move Virtual Machine
Move-VMStorage -ComputerName "$ClusterNode" -VMName "$VMName" -DestinationStoragePath "C:\ClusterStorage\$Volume\$VMName"
Write-Host "Done."
}
} |
# Target Volume
$TargetVolume = "Volume8"
# Get Cluster Nodes
$ClusterNodes = Get-Cluster | Get-ClusterNode
foreach ($ClusterNode in $ClusterNodes)
{
# Clear Variables
$VMs = $Null;
# Get All Virtual Machines on Target Volume
$VMs = Get-VM -ComputerName "$ClusterNode" | Where ConfigurationLocation -like "C:\ClusterStorage\$TargetVolume*"
foreach ($VM in $VMs)
{
# Get VM Information
$VMName = $VM.Name
Write-Host " "
Write-Host "Working on $VMName .."
Write-Host "Hyper-V Host: $ClusterNode"
# Get Volume Information
$Volume = ((Get-ClusterSharedVolume | Where {$_.SharedVolumeInfo.FriendlyVolumeName -notlike "*$TargetVolume*"} | Select -ExpandProperty SharedVolumeInfo | Select @{label="Name";expression={(($_.FriendlyVolumeName).Split("\"))[-1]}},@{label="FreeSpace";expression={($_ | Select -Expand Partition).FreeSpace}} | Sort FreeSpace -Descending)[0]).Name
# Move Virtual Machine
Move-VMStorage -ComputerName "$ClusterNode" -VMName "$VMName" -DestinationStoragePath "C:\ClusterStorage\$Volume\$VMName"
Write-Host "Done."
}
}
That will move all virtual machines via Storage Live Migration into best available CSV volume.
Posted in Virtual Machine Manager, Windows Powershell, Windows Server | No Comment | 1,947 views | 08/06/2014 21:56
Your Hyper-V storage environment crashed and you restored it from a backup or volume snapshot or you want to restore all virtual machines to the Disaster Center via Storage Replication. You added new storage luns into Failover Cluster but CSV names are changed. In that case you may not be able to import Virtual Machines into Hyper-V quickly due to incorrect virtual disk paths.
This script will check all Virtual Machine configs and shows you which configuration is not correct.
Then you can fix it manually or you can fix it via this script.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
| function Repair-VMConfig {
<#
.SYNOPSIS
Function to repair Virtual Machine config after CSV volume path changes.
You should put a volume identifier into CSV volumes to be able to use this script.
Simply put a Volume1.CSV into Volume1 as a volume identifier.
.DESCRIPTION
In some cases, CSV volume names could be changed. So you may not start your virtual machines due to incorrect virtual disk paths.
This function is written to fix this issue. It finds all Virtual Machines configuration problems and shows you as a output.
If you use ApplyChanges switch, it stops all Hyper-V Management Services in all Cluster Nodes, backups VM configs and fix incorrect VM virtual disk paths. Restarts Hyper-V Management Services after changes.
You should put a volume identifier into CSV volumes to be able to use this script. Simply put a Volume1.CSV into Volume1 as a volume identifier.
.PARAMETER WhatIf
Display what would happen if you would run the function with given parameters.
.PARAMETER Confirm
Prompts for confirmation for each operation. Allow user to specify Yes/No to all option to stop prompting.
.EXAMPLE
Repair-VMConfig
Just shows VM configuration problems and do not take any action unless you add -ApplyChanges switch.
.EXAMPLE
Repair-VMConfig -ApplyChanges
Stops all Hyper-V Management Services in all Cluster Nodes, backups VM configs and fix incorrect VM virtual disk paths. Restarts Hyper-V Management Services after changes.
.INPUTS
ApplyChanges switch to apply changes on Virtual Machines.
.OUTPUTS
None
.NOTES
Author: Yusuf Ozturk
Website: http://www.yusufozturk.info
Email: ysfozy@gmail.com
Date created: 07-June-2014
Last modified: 08-June-2014
Version: 1.2
.LINK
http://www.yusufozturk.info
http://twitter.com/yusufozturk
#>
[CmdletBinding(SupportsShouldProcess = $true)]
param (
# Do you confirm changes on Virtual Machines?
[Parameter(
Mandatory = $false,
HelpMessage = 'Do you confirm changes on Virtual Machines?')]
[switch]$ApplyChanges = $false,
# Debug Mode
[Parameter(
Mandatory = $false,
HelpMessage = 'Debug Mode')]
[switch]$DebugMode = $false
)
if ($ApplyChanges -eq $True)
{
# Get Cluster Node List
$ClusterNodes = Get-Cluster | Get-ClusterNode
foreach ($ClusterNode in $ClusterNodes)
{
# Stop Hyper-V Management Service
Invoke-Command -ComputerName $ClusterNode -ScriptBlock {
Set-Service "vmms" -StartupType Disabled
Stop-Service "vmms"
}
}
}
# Read All CSV Directories
$CSVVolumes = Get-ChildItem "C:\ClusterStorage"
foreach ($CSVVolume in $CSVVolumes)
{
# Informational Output
Write-Host " "
Write-Host " "
Write-Host "Working on $CSVVolume .." -ForegroundColor Cyan
Write-Host " "
# CSV Volume Name
$CSVVolumeName = $CSVVolume.Name
# Read All VM Config Files
$VMConfigs = Get-ChildItem "C:\ClusterStorage\$CSVVolumeName" -Filter "*.xml" -Recurse
foreach ($VMConfig in $VMConfigs)
{
# Set Replace VM Config Identifier
$ReplaceVMConfig = "0"
# Get VM Config Full Path
$VMConfigPath = $VMConfig.FullName
if ($ApplyChanges -eq $True)
{
# VM Config Backup Name
$VMConfigBackupPath = $VMConfigPath + ".backup"
# Backup Old VM Config
$BackupVMConfig = Copy-Item -Path $VMConfigPath -Destination $VMConfigBackupPath
}
# Informational Output
Write-Host " "
Write-Host "Working on $VMConfigPath .." -ForegroundColor Yellow
# CSV Volume Regex Pattern
$Pattern = '<pathname type="string">(.*?)</pathname>'
# Get VM Config Content
$VMConfigContent = Get-Content "$VMConfigPath"
# Get CSV Volume Name
$RegexResults = $VMConfigContent -match $Pattern
foreach ($RegexResult in $RegexResults)
{
# Search for Virtual Disks
$RegexSearch = [string]$RegexResult -match $Pattern
# Get Virtual Disk Path
$VHDPath = $Null;
$VHDPath = $Matches[1]
if ($VHDPath)
{
# Check VHD Path
$TestVHDPath = $Null;
$TestVHDPath = Test-Path "$VHDPath"
if (!$TestVHDPath)
{
Write-Host "Error: $VHDPath" -ForegroundColor Red
# CSV Volume Pattern
$CSVPattern = "volume\d{1,2}"
# Search for CSV Volume Name
$RegexSearch = [string]$RegexResult -match $CSVPattern
# Get CSV Volume Name
$CSVName = $Null;
$CSVName = $Matches[0]
if ($CSVName -like "Volume*")
{
# Get CSV Volume Filter
$CSVVolumeFilter = "*" + $CSVName + ".CSV"
# Find CSV Directory
$GetCSVDirectory = $Null;
$GetCSVDirectory = (Get-ChildItem "C:\ClusterStorage" -Filter "$CSVVolumeFilter" -Recurse).Directory.FullName
if ($GetCSVDirectory -ne $Null)
{
# Get Config Content
$ConfigContent = Get-Content -Path "$VMConfigPath"
# Get New CSV Volume
$NewCSVVolume = $GetCSVDirectory.Split("\")[-1]
# Check CSV Volume
if ($NewCSVVolume -like "Volume*")
{
$NewCSVVolume = "TEMPORARY_VOLUME" + $NewCSVVolume.Substring("0,6")
if ($ApplyChanges -eq $True)
{
# Replace CSV Volume
$Regex = New-Object Text.RegularExpressions.Regex $CSVName, "None"
$NewConfigContent = $Regex.Replace($ConfigContent, $NewCSVVolume)
# Save Changes on Config
Set-Content -Value "$NewConfigContent" -Path "$VMConfigPath" -Encoding "BigEndianUnicode"
# Set Replace VM Config Identifier
$ReplaceVMConfig = "1"
}
}
else
{
Write-Host "Error: Could not get volume name of $VHDPath" -ForegroundColor Red
}
}
else
{
Write-Host "Error: Could not find any volume identifier for $CSVName" -ForegroundColor Red
}
}
else
{
Write-Host "Error: $VHDPath is not on a CSV Volume" -ForegroundColor Red
}
}
else
{
Write-Host "OK: $VHDPath" -ForegroundColor Green
}
}
}
if ($ApplyChanges -eq $True)
{
if ($ReplaceVMConfig -eq "1")
{
# Get Config Content
$ConfigContent = Get-Content -Path "$VMConfigPath"
# CSV Volume Prefix
$CSVVolumePrefix = "Volume"
# Replace CSV Volume
$Regex = New-Object Text.RegularExpressions.Regex "TEMPORARY_VOLUME", "None"
$NewConfigContent = $Regex.Replace($ConfigContent, $CSVVolumePrefix)
# Save Changes on Config
Set-Content -Value "$NewConfigContent" -Path "$VMConfigPath" -Encoding "BigEndianUnicode"
}
}
}
}
if ($ApplyChanges -eq $True)
{
foreach ($ClusterNode in $ClusterNodes)
{
# Start Hyper-V Management Service
Invoke-Command -ComputerName $ClusterNode -ScriptBlock {
Set-Service "vmms" -StartupType Automatic
Start-Service "vmms"
}
}
}
} |
function Repair-VMConfig {
<#
.SYNOPSIS
Function to repair Virtual Machine config after CSV volume path changes.
You should put a volume identifier into CSV volumes to be able to use this script.
Simply put a Volume1.CSV into Volume1 as a volume identifier.
.DESCRIPTION
In some cases, CSV volume names could be changed. So you may not start your virtual machines due to incorrect virtual disk paths.
This function is written to fix this issue. It finds all Virtual Machines configuration problems and shows you as a output.
If you use ApplyChanges switch, it stops all Hyper-V Management Services in all Cluster Nodes, backups VM configs and fix incorrect VM virtual disk paths. Restarts Hyper-V Management Services after changes.
You should put a volume identifier into CSV volumes to be able to use this script. Simply put a Volume1.CSV into Volume1 as a volume identifier.
.PARAMETER WhatIf
Display what would happen if you would run the function with given parameters.
.PARAMETER Confirm
Prompts for confirmation for each operation. Allow user to specify Yes/No to all option to stop prompting.
.EXAMPLE
Repair-VMConfig
Just shows VM configuration problems and do not take any action unless you add -ApplyChanges switch.
.EXAMPLE
Repair-VMConfig -ApplyChanges
Stops all Hyper-V Management Services in all Cluster Nodes, backups VM configs and fix incorrect VM virtual disk paths. Restarts Hyper-V Management Services after changes.
.INPUTS
ApplyChanges switch to apply changes on Virtual Machines.
.OUTPUTS
None
.NOTES
Author: Yusuf Ozturk
Website: http://www.yusufozturk.info
Email: ysfozy@gmail.com
Date created: 07-June-2014
Last modified: 08-June-2014
Version: 1.2
.LINK
http://www.yusufozturk.info
http://twitter.com/yusufozturk
#>
[CmdletBinding(SupportsShouldProcess = $true)]
param (
# Do you confirm changes on Virtual Machines?
[Parameter(
Mandatory = $false,
HelpMessage = 'Do you confirm changes on Virtual Machines?')]
[switch]$ApplyChanges = $false,
# Debug Mode
[Parameter(
Mandatory = $false,
HelpMessage = 'Debug Mode')]
[switch]$DebugMode = $false
)
if ($ApplyChanges -eq $True)
{
# Get Cluster Node List
$ClusterNodes = Get-Cluster | Get-ClusterNode
foreach ($ClusterNode in $ClusterNodes)
{
# Stop Hyper-V Management Service
Invoke-Command -ComputerName $ClusterNode -ScriptBlock {
Set-Service "vmms" -StartupType Disabled
Stop-Service "vmms"
}
}
}
# Read All CSV Directories
$CSVVolumes = Get-ChildItem "C:\ClusterStorage"
foreach ($CSVVolume in $CSVVolumes)
{
# Informational Output
Write-Host " "
Write-Host " "
Write-Host "Working on $CSVVolume .." -ForegroundColor Cyan
Write-Host " "
# CSV Volume Name
$CSVVolumeName = $CSVVolume.Name
# Read All VM Config Files
$VMConfigs = Get-ChildItem "C:\ClusterStorage\$CSVVolumeName" -Filter "*.xml" -Recurse
foreach ($VMConfig in $VMConfigs)
{
# Set Replace VM Config Identifier
$ReplaceVMConfig = "0"
# Get VM Config Full Path
$VMConfigPath = $VMConfig.FullName
if ($ApplyChanges -eq $True)
{
# VM Config Backup Name
$VMConfigBackupPath = $VMConfigPath + ".backup"
# Backup Old VM Config
$BackupVMConfig = Copy-Item -Path $VMConfigPath -Destination $VMConfigBackupPath
}
# Informational Output
Write-Host " "
Write-Host "Working on $VMConfigPath .." -ForegroundColor Yellow
# CSV Volume Regex Pattern
$Pattern = '<pathname type="string">(.*?)</pathname>'
# Get VM Config Content
$VMConfigContent = Get-Content "$VMConfigPath"
# Get CSV Volume Name
$RegexResults = $VMConfigContent -match $Pattern
foreach ($RegexResult in $RegexResults)
{
# Search for Virtual Disks
$RegexSearch = [string]$RegexResult -match $Pattern
# Get Virtual Disk Path
$VHDPath = $Null;
$VHDPath = $Matches[1]
if ($VHDPath)
{
# Check VHD Path
$TestVHDPath = $Null;
$TestVHDPath = Test-Path "$VHDPath"
if (!$TestVHDPath)
{
Write-Host "Error: $VHDPath" -ForegroundColor Red
# CSV Volume Pattern
$CSVPattern = "volume\d{1,2}"
# Search for CSV Volume Name
$RegexSearch = [string]$RegexResult -match $CSVPattern
# Get CSV Volume Name
$CSVName = $Null;
$CSVName = $Matches[0]
if ($CSVName -like "Volume*")
{
# Get CSV Volume Filter
$CSVVolumeFilter = "*" + $CSVName + ".CSV"
# Find CSV Directory
$GetCSVDirectory = $Null;
$GetCSVDirectory = (Get-ChildItem "C:\ClusterStorage" -Filter "$CSVVolumeFilter" -Recurse).Directory.FullName
if ($GetCSVDirectory -ne $Null)
{
# Get Config Content
$ConfigContent = Get-Content -Path "$VMConfigPath"
# Get New CSV Volume
$NewCSVVolume = $GetCSVDirectory.Split("\")[-1]
# Check CSV Volume
if ($NewCSVVolume -like "Volume*")
{
$NewCSVVolume = "TEMPORARY_VOLUME" + $NewCSVVolume.Substring("0,6")
if ($ApplyChanges -eq $True)
{
# Replace CSV Volume
$Regex = New-Object Text.RegularExpressions.Regex $CSVName, "None"
$NewConfigContent = $Regex.Replace($ConfigContent, $NewCSVVolume)
# Save Changes on Config
Set-Content -Value "$NewConfigContent" -Path "$VMConfigPath" -Encoding "BigEndianUnicode"
# Set Replace VM Config Identifier
$ReplaceVMConfig = "1"
}
}
else
{
Write-Host "Error: Could not get volume name of $VHDPath" -ForegroundColor Red
}
}
else
{
Write-Host "Error: Could not find any volume identifier for $CSVName" -ForegroundColor Red
}
}
else
{
Write-Host "Error: $VHDPath is not on a CSV Volume" -ForegroundColor Red
}
}
else
{
Write-Host "OK: $VHDPath" -ForegroundColor Green
}
}
}
if ($ApplyChanges -eq $True)
{
if ($ReplaceVMConfig -eq "1")
{
# Get Config Content
$ConfigContent = Get-Content -Path "$VMConfigPath"
# CSV Volume Prefix
$CSVVolumePrefix = "Volume"
# Replace CSV Volume
$Regex = New-Object Text.RegularExpressions.Regex "TEMPORARY_VOLUME", "None"
$NewConfigContent = $Regex.Replace($ConfigContent, $CSVVolumePrefix)
# Save Changes on Config
Set-Content -Value "$NewConfigContent" -Path "$VMConfigPath" -Encoding "BigEndianUnicode"
}
}
}
}
if ($ApplyChanges -eq $True)
{
foreach ($ClusterNode in $ClusterNodes)
{
# Start Hyper-V Management Service
Invoke-Command -ComputerName $ClusterNode -ScriptBlock {
Set-Service "vmms" -StartupType Automatic
Start-Service "vmms"
}
}
}
}
You should add -ApplyChanges switch in order to confirm changes on virtual machine configs. That will backup all VM configs before modification. Also that will stop and start all Hyper-V Management Services in all cluster nodes due to changes on VM configs.
Posted in Virtual Machine Manager, Windows Powershell, Windows Server | No Comment | 1,907 views | 06/06/2014 08:39
You can migrate selected Virtual Machines in a Hyper-V Cluster with following script.
That will migrate all virtual machines into target volume.
If target volume is not specified, it will look for best available CSV volume, and migrate virtual machine into that.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
| # Target Volume
$TargetVolume = "Volume1"
# Create VM Array
$VMArray = New-Object System.Collections.ArrayList
$VMArray.Clear();
$AddArray = $VMArray.Add("VM01")
$AddArray = $VMArray.Add("VM02")
$AddArray = $VMArray.Add("VM03")
# Get Cluster Nodes
$ClusterNodes = Get-Cluster | Get-ClusterNode
foreach ($ClusterNode in $ClusterNodes)
{
# Get All Virtual Machines
$VMs = Get-VM -ComputerName "$ClusterNode"
foreach ($VM in $VMs)
{
# Get VM Information
$VMName = $VM.Name
if ($VMArray.Contains($VMName) -eq $True)
{
Write-Host " "
Write-Host "Working on $VMName .."
Write-Host "Hyper-V Host: $ClusterNode"
if (!$TargetVolume)
{
# Get Volume Information
$Volume = ((Get-ClusterSharedVolume | Select -ExpandProperty SharedVolumeInfo | Select @{label="Name";expression={(($_.FriendlyVolumeName).Split("\"))[-1]}},@{label="FreeSpace";expression={($_ | Select -Expand Partition).FreeSpace}} | Sort FreeSpace -Descending)[0]).Name
}
else
{
$Volume = $TargetVolume
}
# Move Virtual Machine
Move-VMStorage -ComputerName "$ClusterNode" -VMName "$VMName" -DestinationStoragePath "C:\ClusterStorage\$Volume\$VMName"
Write-Host "Done."
}
}
} |
# Target Volume
$TargetVolume = "Volume1"
# Create VM Array
$VMArray = New-Object System.Collections.ArrayList
$VMArray.Clear();
$AddArray = $VMArray.Add("VM01")
$AddArray = $VMArray.Add("VM02")
$AddArray = $VMArray.Add("VM03")
# Get Cluster Nodes
$ClusterNodes = Get-Cluster | Get-ClusterNode
foreach ($ClusterNode in $ClusterNodes)
{
# Get All Virtual Machines
$VMs = Get-VM -ComputerName "$ClusterNode"
foreach ($VM in $VMs)
{
# Get VM Information
$VMName = $VM.Name
if ($VMArray.Contains($VMName) -eq $True)
{
Write-Host " "
Write-Host "Working on $VMName .."
Write-Host "Hyper-V Host: $ClusterNode"
if (!$TargetVolume)
{
# Get Volume Information
$Volume = ((Get-ClusterSharedVolume | Select -ExpandProperty SharedVolumeInfo | Select @{label="Name";expression={(($_.FriendlyVolumeName).Split("\"))[-1]}},@{label="FreeSpace";expression={($_ | Select -Expand Partition).FreeSpace}} | Sort FreeSpace -Descending)[0]).Name
}
else
{
$Volume = $TargetVolume
}
# Move Virtual Machine
Move-VMStorage -ComputerName "$ClusterNode" -VMName "$VMName" -DestinationStoragePath "C:\ClusterStorage\$Volume\$VMName"
Write-Host "Done."
}
}
}
This script should work even if you have pass-through disks on virtual machine or virtual hba.
Posted in Virtual Machine Manager, Windows Powershell, Windows Server | No Comment | 1,154 views | 05/06/2014 15:55
If you need to get Windows OS and Version information of a list of servers, then you can use following script.
1
2
3
4
5
6
7
8
9
10
| $Servers = Get-Content Servers.txt
foreach ($Server in $Servers)
{
$OSInfo = $Null;
$CompInfo = $Null;
$OSInfo = Get-WmiObject -ComputerName $Server -Class "Win32_OperatingSystem"
$CompInfo = Get-WmiObject -ComputerName $Server -Class "Win32_ComputerSystem"
$Value = $Server + ";" + $OSInfo.Caption + ";" + $OSInfo.CSDVersion + ";" + $OSInfo.BuildNumber + ";" + $OSInfo.Version + ";" + $CompInfo.Manufacturer + ";" + $CompInfo.Model
Add-Content -Value $Value -Path ServerOSInfo.txt
} |
$Servers = Get-Content Servers.txt
foreach ($Server in $Servers)
{
$OSInfo = $Null;
$CompInfo = $Null;
$OSInfo = Get-WmiObject -ComputerName $Server -Class "Win32_OperatingSystem"
$CompInfo = Get-WmiObject -ComputerName $Server -Class "Win32_ComputerSystem"
$Value = $Server + ";" + $OSInfo.Caption + ";" + $OSInfo.CSDVersion + ";" + $OSInfo.BuildNumber + ";" + $OSInfo.Version + ";" + $CompInfo.Manufacturer + ";" + $CompInfo.Model
Add-Content -Value $Value -Path ServerOSInfo.txt
}
That will output all data into ServerOSInfo.txt file.
|