Skip to content

Commit 4dcea83

Browse files
authored
First commit of Hyper-V and SQL Server
1 parent b97bcb9 commit 4dcea83

File tree

2 files changed

+283
-0
lines changed

2 files changed

+283
-0
lines changed
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
##############################################################################################################################
2+
# Hyper-V Cluster Shared Volume (CSV) with SQL Server Snapshot Example
3+
#
4+
# Scenario:
5+
# This script will clone a Hyper-V Cluster Shared Volume (CSV), using a crash consistent snapshot, and present it back
6+
# to the originating Hyper-V cluster as a second CSV "copy."
7+
#
8+
# This example scenario is useful if you have isolated a VLDB SQL Server database exclusively onto this CSV
9+
#
10+
# See https://github.com/PureStorage-OpenConnect/sqlserver-scripts/tree/master/demos-sdk2/Hyper-V%20CSV%20Snapshot
11+
# for more details
12+
#
13+
#
14+
# Prerequisities:
15+
# 1. An additional Windows server (referred to as a staging server). This staging server does not have to be a
16+
# Hyper-V host.
17+
# 2. A pre-created volume of equal size to the source CSV, pre-attached to the staging server.
18+
# 3. The 'Failover Cluster Module for Windows PowerShell' Feature in Windows is required on the Hyper-V host.
19+
# Add-WindowsFeature RSAT-Clustering-PowerShell
20+
#
21+
#
22+
# Usage Notes:
23+
#
24+
# The staging server is needed because each CSV has a unique signature. If the CSV is presented back to the Hyper-V
25+
# host unaltered, a signature collision will be detected and the new CSV will not be able to be used by Windows.
26+
# Hyper-V is unable to resignature in this state either. Instead, the CSV must be presented to another machine (aka
27+
# the staging server), resignatured there, then can be re-snapshotted and cloned back to the originating Hyper-V
28+
# host.
29+
#
30+
# This script may be adjusted to clone and present the CSV snapshot to a different Hyper-V host. If this is done, then
31+
# the staging server and resignature step is not required, since the new target Hyper-V host will not have two of the
32+
# same CSV causing a signature conflict.
33+
#
34+
#
35+
# Disclaimer:
36+
# This example script is provided AS-IS and meant to be a building block to be adapted to fit an individual
37+
# organization's infrastructure.
38+
##############################################################################################################################
39+
Import-Module PureStoragePowerShellSDK2
40+
41+
42+
43+
# Variables
44+
$FlashArrayEndPoint = 'flasharray1.example.com'
45+
$SourceVMCluster = 'hyperv-cluster-01.fsa.lab'
46+
$SourceVMHost = 'hyperv-host-01.example.com'
47+
$SourceVM = 'hyperv-vm-source' # No FQDN
48+
$SourceVolumeName = 'hyperv-vm-source-csv-01' # Name of the volume in FlashArray
49+
$StagingServer = 'windows-staging-server'
50+
$StagingVolumeName = 'temporary-volume-for-csv-resignature'
51+
$StagingDiskSerialNumber = '6000c2945ce069b03b9750d2afe72828'
52+
$TargetVMHost = 'hyperv-host-02.example.com' # No FQDN
53+
$TargetVM = 'hyperv-vm-target' # No FQDN
54+
$TargetVolumeName = 'hyperv-vm-target-csv-01-cloned'
55+
$TargetClusterDiskNumber = 'Cluster Disk 3'
56+
$DatabaseName = 'MyDatabaseName'
57+
$ClusteredStorageFolder = "C:\ClusterStorage\volume4\hv-sqldata-01\data\*.*" # Target Host Folder containing cloned VHDX/AVHDX files
58+
59+
60+
61+
# Establish credential to use for all connections
62+
$Credential = Get-Credential -Message 'Enter your Pure credentials'
63+
64+
65+
66+
# Connect to the FlashArray
67+
$FlashArray = Connect-Pfa2Array -Endpoint $FlashArrayEndPoint -Credential ($Credential) -IgnoreCertificateError
68+
69+
70+
71+
# Determine which Hyper-V node each role currently resides on
72+
$HyperVClusterSession = New-PSSession -ComputerName $SourceVMCluster -Credential $Credential
73+
74+
$SourceClusterGroup = Invoke-Command -Session $HyperVClusterSession -ScriptBlock { Get-ClusterGroup -Name $Using:SourceVM }
75+
$TargetClusterGroup = Invoke-Command -Session $HyperVClusterSession -ScriptBlock { Get-ClusterGroup -Name $Using:TargetVM }
76+
77+
$SourceVMHost = $SourceClusterGroup.OwnerNode
78+
$TargetVMHost = $TargetClusterGroup.OwnerNode
79+
80+
# Verify
81+
$SourceVM
82+
$SourceVMHost
83+
84+
$TargetVM
85+
$TargetVMHost
86+
87+
88+
89+
# Prepare the staging CSV for overlay
90+
# Connect to staging VM
91+
$StagingServerSession = New-PSSession -ComputerName $StagingServer -Credential $Credential
92+
93+
94+
95+
# Offline the volume
96+
# NOTE: use Get-Disk prior to get the correct Serial Number
97+
Invoke-Command -Session $StagingServerSession -ScriptBlock { Get-Disk | Where-Object { $_.SerialNumber -eq $using:StagingDiskSerialNumber } | Set-Disk -IsOffline $True }
98+
99+
# Verify
100+
Invoke-Command -Session $StagingServerSession -ScriptBlock { Get-Disk | Where-Object { $_.SerialNumber -eq $using:StagingDiskSerialNumber }}
101+
102+
103+
104+
# Snapshot the source CSV
105+
# This example is for an on-demand snapshot. Can adjust code to also use a prior snapshot; ex. regularly scheduled
106+
# snapshots or an asynchronously replicated snapshot from another FlashArray
107+
108+
# Clone the source CSV to the staging CSV
109+
New-Pfa2Volume -Array $FlashArray -Name $StagingVolumeName -SourceName $SourceVolumeName -Overwrite $true
110+
111+
112+
113+
# Now must resignature the CSV on the staging VM
114+
# Build DISKPART script commands for resignature
115+
$StagingDisk = Invoke-Command -Session $StagingServerSession -ScriptBlock { Get-Disk | Where-Object { $_.SerialNumber -eq $Using:StagingDiskSerialNumber }}
116+
$DiskNumber = $StagingDisk.Number
117+
$NewUniqueID = [GUID]::NewGuid()
118+
$Commands = "`"SELECT DISK $DiskNumber`"",
119+
"`"UNIQUEID DISK ID=$NewUniqueID`""
120+
$ScriptBlock = [string]::Join(",",$Commands)
121+
$DiskpartScriptBlock = $ExecutionContext.InvokeCommand.NewScriptBlock("$ScriptBlock | DISKPART")
122+
123+
# Verify DISKPART command
124+
$DiskpartScriptBlock
125+
126+
# Issue resignature command
127+
Invoke-Command -Session $StagingServerSession -ScriptBlock $DiskpartScriptBlock
128+
129+
130+
131+
# Prepare target VM
132+
$TargetVMSession = New-PSSession -ComputerName $TargetVM -Credential $Credential
133+
134+
# Offline the database
135+
$Query = "ALTER DATABASE $DatabaseName SET OFFLINE WITH ROLLBACK IMMEDIATE"
136+
Invoke-Command -Session $TargetVMSession -ScriptBlock {Param($querytask) Invoke-Sqlcmd -ServerInstance . -Database master -Query $querytask} -ArgumentList ($Query)
137+
138+
# Offline the volume
139+
# Because this is a Hyper-V VM, volume serial numbers are not populated by Hyper-V into a virtual machine
140+
# Therefore must use a different method identify the proper volume to offline
141+
142+
# Confirm which drive you want
143+
Invoke-Command -Session $TargetVMSession -ScriptBlock { Get-Disk | Format-Table }
144+
145+
146+
147+
# Specify the drive number
148+
$DiskNumber = 1
149+
Invoke-Command -Session $TargetVMSession -ScriptBlock { Get-Disk -Number $using:DiskNumber | Get-Disk | Set-Disk -IsOffline $True }
150+
151+
# Verify offline
152+
Invoke-Command -Session $TargetVMSession -ScriptBlock { Get-Disk | Format-Table }
153+
154+
155+
156+
# Prepare target VM Host
157+
$TargetVMHostSession = New-PSSession -ComputerName $TargetVMHost -Credential $Credential
158+
159+
# Remove SQL Server cluster resource dependency on database volume
160+
# Only use this if you are using Clustered Disks (NOT Clustered Shared Volumes)
161+
# Invoke-Command -Session $TargetVMHostSession -ScriptBlock { Get-ClusterResource 'SQL Server' | Remove-ClusterResourceDependency $TargetVolumeName }
162+
163+
# Stop the disk cluster resource
164+
# NOTE: need to know which Cluster Disk Number first
165+
# This will put the target Hyper-V VM into a Saved state
166+
Invoke-Command -Session $TargetVMHostSession -ScriptBlock { Stop-ClusterResource $Using:TargetClusterDiskNumber }
167+
168+
# Verify
169+
Invoke-Command -Session $TargetVMHostSession -ScriptBlock { Get-ClusterSharedVolume $Using:TargetClusterDiskNumber }
170+
171+
172+
173+
# Clone the staging CSV to the target CSV
174+
New-Pfa2Volume -Array $FlashArray -Name $TargetVolumeName -SourceName $StagingVolumeName -Overwrite $true
175+
176+
177+
178+
# Start the disk cluster resource
179+
Invoke-Command -Session $TargetVMHostSession -ScriptBlock { Start-ClusterResource $Using:TargetClusterDiskNumber }
180+
181+
# Verify
182+
Invoke-Command -Session $TargetVMHostSession -ScriptBlock { Get-ClusterSharedVolume $Using:TargetClusterDiskNumber }
183+
184+
185+
186+
# Must now update permissions in Windows to grant the new VM access to the VHDX files
187+
188+
Invoke-Command -Session $TargetVMHostSession -ScriptBlock {
189+
$VMID = "NT VIRTUAL MACHINE\"
190+
Get-VM -name $Using:TargetVM | Select-Object -ExpandProperty VMID
191+
192+
$fileAclList = Get-Acl $Using:ClusteredStorageFolder
193+
Foreach ($acl in $fileAclList) {
194+
# Add a new rule to grant full control to a user
195+
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule($VMID, "FullControl", "Allow")
196+
$acl.AddAccessRule($rule)
197+
Set-Acl -Path $acl.PSPath -AclObject $acl
198+
}
199+
}
200+
201+
202+
203+
# Online the volume
204+
205+
# Confirm which drive you want
206+
Invoke-Command -Session $TargetVMSession -ScriptBlock { Get-Disk | Format-Table }
207+
208+
209+
210+
# Specify the drive number
211+
$DiskNumber = 1
212+
Invoke-Command -Session $TargetVMSession -ScriptBlock { Get-Disk -Number $using:DiskNumber | Get-Disk | Set-Disk -IsOffline $False }
213+
214+
# Verify
215+
Invoke-Command -Session $TargetVMSession -ScriptBlock { Get-Disk | Format-Table }
216+
217+
218+
219+
# Online the database
220+
$Query = "ALTER DATABASE $DatabaseName SET ONLINE"
221+
Invoke-Command -Session $TargetVMSession -ScriptBlock {Param($querytask) Invoke-Sqlcmd -ServerInstance . -Database master -Query $querytask} -ArgumentList ($Query)
222+
223+
# Verify
224+
$Query = "SELECT @@SERVERNAME, name, state_desc, GETDATE() FROM sys.databases WHERE database_id = DB_ID('$DatabaseName')"
225+
Invoke-Command -Session $TargetVMSession -ScriptBlock {Param($querytask) Invoke-Sqlcmd -ServerInstance . -Database master -Query $querytask} -ArgumentList ($Query)
226+
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
**Hyper-V Cluster Shared Volume + SQL Server Snapshot Scripts**
2+
<p align="center"></p>
3+
This folder contains Hyper-V Cluster Shared Volume + SQL Server example snapshot scripts.
4+
5+
**Files:**
6+
- Hyper-V CSV w SQL Server Snapshot.ps1
7+
8+
<!-- wp:separator -->
9+
<hr class="wp-block-separator"/>
10+
<!-- /wp:separator -->
11+
12+
**Scenario:**
13+
<BR>This example script shows steps to snapshot a Hyper-V Cluster Shared Volume (CSV) that contains data & log VHDX/AVHDX files for a SQL Server.
14+
15+
<BR>
16+
<BR>
17+
This scenario has a SQL Server Hyper-V VM (ex: Production) with at least two CSVs. The first CSV is the primary, which will contain VHDX files for the VM itself, OS drive, SQL Server system files, tempdb, etc. The second CSV, which is what this script will clone, will ONLY contain the data and log files for 1 or more user databases ONLY. The data and log files can be on two different VHDX files or the same VHDX file - it only matters that they reside in this second CSV, not the first.
18+
<BR>
19+
<BR>
20+
The second SQL Server Hyper-V VM (ex: non-Production) will also be set up similarly, with two CSVs, so we can clone the Production CSV's user database(s) and refresh this second VM's second CSV with a clone of Production's database.
21+
<BR>
22+
<BR>
23+
All references to a "source" refer to the production side (VM, CSV, etc).
24+
All references to a "target" refer to the non-production side (VM, CSV, etc).
25+
26+
**Prerequisites:**
27+
1. The production CSV must already be cloned and presented once, to the non-production side.
28+
2. This script assumes the database(s) are already attached on the target, non-production SQL Server.
29+
30+
**Important Usage Notes:**
31+
<BR>You must pre-setup the target VM with a cloned CSV from the source already. You will ONLY be utilizing the specific VHDX(s) that contain the data/log files of interest, from the cloned CSV. Also note that the CSV does not need to only exclusively contain VHDXs for the SQL Server in question. If other VHDXs are present in the CSV, used by the either the source SQL Server VM or other VMs, they do not need to be deleted or otherwise manipulated during this cloning process. Remember FlashArray deduplicates data, thus a clone's set of additional, unused VHDXs will not have a negative impact.
32+
33+
For the cloned CSV pre-setup, you can use subsets of the code below to clone the source CSV, present it to the target server, then attach the VHDX(s) containing the production databases that will be re-cloned with this script. Once "staged," you can then use this script fully to refresh the data files in the cloned CSV that is attached to the target server.
34+
35+
This script also assumes that all database files (data and log) are on the same volume/single VHDX. If multiple volumes/VHDXs are being used, you will have to adjust the code (ex: add additional foreach loops for manipulating multiple VHDXs).
36+
37+
The staging server is needed because each CSV has a unique signature. If the CSV is presented back to the Hyper-V host unaltered, a signature collision will be detected and the new CSV will not be able to be used by Windows. Hyper-V is unable to resignature in this state either. Instead, the CSV must be presented to another machine (aka the staging server), resignatured there, then can be re-snapshotted and cloned back to the originating Hyper-V host.
38+
39+
This script may be adjusted to clone and present the CSV snapshot to a different Hyper-V host. If this is done, then the staging server and resignature step is not required, since the new target Hyper-V host will not have two of the same CSV causing a signature conflict.
40+
41+
<!-- wp:separator -->
42+
<hr class="wp-block-separator"/>
43+
<!-- /wp:separator -->
44+
45+
**Disclaimer:**
46+
<BR>
47+
This example script is provided AS-IS and meant to be a building block to be adapted to fit an individual organization's infrastructure.
48+
<BR>
49+
<BR>
50+
51+
We encourage the modification and expansion of these scripts by the community. Although not necessary, please issue a Pull Request (PR) if you wish to request merging your modified code in to this repository.
52+
53+
<!-- wp:separator -->
54+
<hr class="wp-block-separator"/>
55+
<!-- /wp:separator -->
56+
57+
_The contents of the repository are intended as examples only and should be modified to work in your individual environments. No script examples should be used in a production environment without fully testing them in a development or lab environment. There are no expressed or implied warranties or liability for the use of these example scripts and templates presented by Pure Storage and/or their creators._

0 commit comments

Comments
 (0)