diff --git a/README.md b/README.md index 86abb12..7fe12a4 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,16 @@ Array-based snapshots are used to decouple database operations from the size of | Demo | Description | | | | ----------- | ----------- | ----------- | ----------- | -| **Volume Database Refresh on VMDK Virtual Disks** | Refresh a database from Volume snapshot where all of the databases's files are using a VMware VMDK Virtual Disk type on a single VMFS Datastore on the same Volume on the same FlashArray. | [More Info](./demos-sdk2/VMFS-VMDK%20Snapshot/) | [Sample Code](./demos-sdk2/VMFS-VMDK%20Snapshot/VMFS-VMDK%20Snapshot.ps1) | +| **Volume Database Refresh on VMDK Virtual Disks** | Refresh a database from a Volume snapshot where all of the databases's files are using a VMware VMDK Virtual Disk type on a single VMFS Datastore on the same Volume on the same FlashArray. | [More Info](./demos-sdk2/VMFS-VMDK%20Snapshot/) | [Sample Code](./demos-sdk2/VMFS-VMDK%20Snapshot/VMFS-VMDK%20Snapshot.ps1) | +| **Point in Time Recovery on VMDK Virtual Disks** | Combine a FlashArray snapshot with SQL Server 2022 T-SQL Snapshot Backup on a VMFS datastore for application-consistent snapshots and point-in-time recovery. | [More Info](./demos-sdk2/VMFS-VMDK%20Snapshot/) | [Sample Code](./demos-sdk2/VMFS-VMDK%20Snapshot/Point%20in%20Time%20Recovery%20-%20VMFS.ps1) | + + +## Using Snapshots for Databases on Hyper-V Cluster Shared Volumes (CSV) + +| Demo | Description | | | +| ----------- | ----------- | ----------- | ----------- | +| **Hyper-V CSV Crash-Consistent Clone** | Refresh a dev/test SQL Server from a crash-consistent FlashArray snapshot of a production Hyper-V CSV, with no source downtime. | [More Info](./demos-sdk2/Hyper-V%20CSV%20w%20SQL%20Server%20Snapshot/) | [Sample Code](./demos-sdk2/Hyper-V%20CSV%20w%20SQL%20Server%20Snapshot/HyperV%20CrashConsistentClone.ps1) | +| **Hyper-V CSV T-SQL Snapshot Backup** | Combine a FlashArray volume snapshot with SQL Server 2022 T-SQL Snapshot Backup on a Hyper-V CSV for application-consistent snapshots and point-in-time recovery. | [More Info](./demos-sdk2/Hyper-V%20CSV%20w%20SQL%20Server%20Snapshot/) | [Sample Code](./demos-sdk2/Hyper-V%20CSV%20w%20SQL%20Server%20Snapshot/HyperV-TSQL-SnapshotBackup.ps1) | ## Using ActiveDR diff --git a/demos-sdk2/ActiveDR/ActiveDR Full Failover.ps1 b/demos-sdk2/ActiveDR/ActiveDR Full Failover.ps1 index 73b5d44..ffdc21d 100644 --- a/demos-sdk2/ActiveDR/ActiveDR Full Failover.ps1 +++ b/demos-sdk2/ActiveDR/ActiveDR Full Failover.ps1 @@ -52,7 +52,7 @@ $ProductionSQLServerSession = New-PSSession -ComputerName $ProductionSQLServer # Connect to FlashArray $Credential = Get-Credential -$FlashArray = Connect-Pfa2Array -Endpoint $DRArrayName -Credential $Credential -IgnoreCertificateError +$FlashArray = Connect-Pfa2Array -Endpoint $ProductionArrayName -Credential $Credential -IgnoreCertificateError diff --git a/demos-sdk2/ActiveDR/SQL Server FCI + ActiveDR/ActiveDR-FCI-Testing.ps1 b/demos-sdk2/ActiveDR/SQL Server FCI + ActiveDR/ActiveDR-FCI-Testing.ps1 index f739f62..5a412b0 100644 --- a/demos-sdk2/ActiveDR/SQL Server FCI + ActiveDR/ActiveDR-FCI-Testing.ps1 +++ b/demos-sdk2/ActiveDR/SQL Server FCI + ActiveDR/ActiveDR-FCI-Testing.ps1 @@ -1,30 +1,45 @@ -#################################################################################################################### -#################################################################################################################### -## -## ActiveDR failover testing for SQL Server Failover Cluster Instance -## -## This demo script runs through two scenarios: - -## 1. Failover of clustered SQL Server role to node on same array -## 2. Failover of clustered SQL Server role to node on remote array -## -## The second test involves the following steps: - -###### Stop clustered SQL Server role in FCI -###### Demote source pod -###### Promote target pod -###### Move clustered role to node on target array -###### Start up clustered SQL Server role -## -## Author - Andrew Pruski -## apruski@purestorage.com -## -#################################################################################################################### -#################################################################################################################### +############################################################################################################################## +# ActiveDR Failover Testing for SQL Server Failover Cluster Instance +# +# Scenario: +# This demo script runs through two scenarios: +# 1. Failover of a clustered SQL Server role to a node on the same storage array +# 2. Failover of a clustered SQL Server role to a node on a remote storage array +# +# The remote array failover involves the following steps: +# - Stop the clustered SQL Server role in the FCI +# - Demote the source pod +# - Promote the target pod +# - Move the clustered role to a node on the target array +# - Start the clustered SQL Server role +# +# Disclaimer: +# This example script is provided AS-IS and meant to be a building block to be adapted to fit an individual +# organization's infrastructure. +############################################################################################################################## -# import powershell modules +# Import PowerShell modules Import-Module FailoverClusters -Import-Module PureStoragePowershellSDK2 +Import-Module PureStoragePowerShellSDK2 + + + +# Variables +$ClusterName = "WindowsClusterName" +$ClusterRole = "SQL Server (MSSQLSERVER)" # Clustered SQL Server role name +$NodeSameArray = "NodeOnSameArray" # Cluster node on the same storage array +$NodeRemoteArray = "NodeOnRemoteArray" # Cluster node on the remote storage array +$SourceFlashArrayIp = "flasharray1.example.com" # Source FlashArray endpoint +$SourcePodName = "PodNameOnSourceArray" # Pod name on the source FlashArray +$TargetFlashArrayIp = "flasharray2.example.com" # Target FlashArray endpoint +$TargetPodName = "PodNameOnTargetArray" # Pod name on the target FlashArray + + + +# Set credentials +$PureCred = Get-Credential @@ -36,124 +51,95 @@ Import-Module PureStoragePowershellSDK2 -# set variables -$ClusterName = "WindowsClusterName" -$ClusterRole = "SQL Server (MSSQLSERVER)" -$NodeSameArray = "NodeOnSameArray" - - - -# confirm cluster +# Confirm cluster Get-Cluster $ClusterName -# confirm cluster nodes +# Confirm cluster nodes Get-Cluster $ClusterName | Get-ClusterNode -# confirm clustered SQL Server service +# Confirm clustered SQL Server service Get-ClusterGroup -Cluster $ClusterName -Name $ClusterRole -# test failing over clustered service to node on same storage array +# Test failing over clustered service to node on same storage array Move-ClusterGroup -Cluster $ClusterName -Name $ClusterRole -Node $NodeSameArray -# confirm clustered SQL Server service +# Confirm clustered SQL Server service Get-ClusterGroup -Cluster $ClusterName -Name $ClusterRole -################################################################################################################ +#################################################################################################################### # # Performing failover to node on remote storage array # -################################################################################################################ - - - -# set source array details -$SourceFlashArrayIp = "SourceFlashArrayIpAddress" -$SourcePodName = "PodNameOnSourceArray" - - - -# set Pure credentials -$PureCred = Get-Credentials +#################################################################################################################### -# connect to source flasharray +# Connect to the source FlashArray $SourceFlashArray = Connect-Pfa2Array -EndPoint $SourceFlashArrayIp -Credential $PureCred -IgnoreCertificateError -# confirm pod replication status +# Confirm pod replication status Get-Pfa2PodReplicaLink -Array $SourceFlashArray -LocalPodName $SourcePodName -# confirm clustered SQL Server service +# Confirm clustered SQL Server service Get-ClusterGroup -Cluster $ClusterName -Name $ClusterRole -# stop clustered service - taking volumes offline +# Stop clustered service - taking volumes offline Stop-ClusterGroup -Cluster $ClusterName -Name $ClusterRole -# confirm clustered service offline +# Confirm clustered service offline Get-ClusterGroup -Cluster $ClusterName -Name $ClusterRole -# demote Production Pod with Quiesce +# Demote the source pod with quiesce Update-Pfa2Pod -Array $SourceFlashArray -Name $SourcePodName -Quiesce $True -RequestedPromotionState "demoted" -# confirm Production Pod status - PromotionStatus : demoted +# Confirm source pod status - PromotionStatus : demoted Get-Pfa2Pod -Array $SourceFlashArray -Name $SourcePodName -# set target array details -$TargetFlashArrayIp = "TargetFlashArrayIpAddress" -$TargetPodName = "PodNameOnTargetArray" - - - -# connect to target flasharray +# Connect to the target FlashArray $TargetFlashArray = Connect-Pfa2Array -EndPoint $TargetFlashArrayIp -Credential $PureCred -IgnoreCertificateError -# promote pod +# Promote the target pod Update-Pfa2Pod -Array $TargetFlashArray -Name $TargetPodName -RequestedPromotionState "promoted" -# confirm pod promoted - PromotionStatus : promoted -Get-Pfa2Pod -Array $FlashArray -Name $TargetPodName - - - -# set node name on remote array -$NodeSameArray2 = "NodeOnRemoteArray" +# Confirm pod promoted - PromotionStatus : promoted +Get-Pfa2Pod -Array $TargetFlashArray -Name $TargetPodName -# move clustered role to node on target array -Move-ClusterGroup -Cluster $ClusterName -Name $ClusterRole -Node $NodeSameArray2 +# Move clustered role to node on the target array +Move-ClusterGroup -Cluster $ClusterName -Name $ClusterRole -Node $NodeRemoteArray -# start the clustered role +# Start the clustered role Start-ClusterGroup -Cluster $ClusterName -Name $ClusterRole diff --git a/demos-sdk2/Hyper-V CSV w SQL Server Snapshot/Hyper-V CSV w SQL Server Snapshot.ps1 b/demos-sdk2/Hyper-V CSV w SQL Server Snapshot/Hyper-V CSV w SQL Server Snapshot.ps1 deleted file mode 100644 index d898ae8..0000000 --- a/demos-sdk2/Hyper-V CSV w SQL Server Snapshot/Hyper-V CSV w SQL Server Snapshot.ps1 +++ /dev/null @@ -1,226 +0,0 @@ -############################################################################################################################## -# Hyper-V Cluster Shared Volume (CSV) with SQL Server Snapshot Example -# -# Scenario: -# This script will clone a Hyper-V Cluster Shared Volume (CSV), using a crash consistent snapshot, and present it back -# to the originating Hyper-V cluster as a second CSV "copy." -# -# This example scenario is useful if you have isolated a VLDB SQL Server database exclusively onto this CSV -# -# See https://github.com/PureStorage-OpenConnect/sqlserver-scripts/tree/master/demos-sdk2/Hyper-V%20CSV%20Snapshot -# for more details -# -# -# Prerequisities: -# 1. An additional Windows server (referred to as a staging server). This staging server does not have to be a -# Hyper-V host. -# 2. A pre-created volume of equal size to the source CSV, pre-attached to the staging server. -# 3. The 'Failover Cluster Module for Windows PowerShell' Feature in Windows is required on the Hyper-V host. -# Add-WindowsFeature RSAT-Clustering-PowerShell -# -# -# Usage Notes: -# -# 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. -# -# 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. -# -# -# Disclaimer: -# This example script is provided AS-IS and meant to be a building block to be adapted to fit an individual -# organization's infrastructure. -############################################################################################################################## -Import-Module PureStoragePowerShellSDK2 - - - -# Variables -$FlashArrayEndPoint = 'flasharray1.example.com' -$SourceVMCluster = 'hyperv-cluster-01.fsa.lab' -$SourceVMHost = 'hyperv-host-01.example.com' -$SourceVM = 'hyperv-vm-source' # No FQDN -$SourceVolumeName = 'hyperv-vm-source-csv-01' # Name of the volume in FlashArray -$StagingServer = 'windows-staging-server' -$StagingVolumeName = 'temporary-volume-for-csv-resignature' -$StagingDiskSerialNumber = '6000c2945ce069b03b9750d2afe72828' -$TargetVMHost = 'hyperv-host-02.example.com' # No FQDN -$TargetVM = 'hyperv-vm-target' # No FQDN -$TargetVolumeName = 'hyperv-vm-target-csv-01-cloned' -$TargetClusterDiskNumber = 'Cluster Disk 3' -$DatabaseName = 'MyDatabaseName' -$ClusteredStorageFolder = "C:\ClusterStorage\volume4\hv-sqldata-01\data\*.*" # Target Host Folder containing cloned VHDX/AVHDX files - - - -# Establish credential to use for all connections -$Credential = Get-Credential -Message 'Enter your Pure credentials' - - - -# Connect to the FlashArray -$FlashArray = Connect-Pfa2Array -Endpoint $FlashArrayEndPoint -Credential ($Credential) -IgnoreCertificateError - - - -# Determine which Hyper-V node each role currently resides on -$HyperVClusterSession = New-PSSession -ComputerName $SourceVMCluster -Credential $Credential - -$SourceClusterGroup = Invoke-Command -Session $HyperVClusterSession -ScriptBlock { Get-ClusterGroup -Name $Using:SourceVM } -$TargetClusterGroup = Invoke-Command -Session $HyperVClusterSession -ScriptBlock { Get-ClusterGroup -Name $Using:TargetVM } - -$SourceVMHost = $SourceClusterGroup.OwnerNode -$TargetVMHost = $TargetClusterGroup.OwnerNode - -# Verify -$SourceVM -$SourceVMHost - -$TargetVM -$TargetVMHost - - - -# Prepare the staging CSV for overlay -# Connect to staging VM -$StagingServerSession = New-PSSession -ComputerName $StagingServer -Credential $Credential - - - -# Offline the volume -# NOTE: use Get-Disk prior to get the correct Serial Number -Invoke-Command -Session $StagingServerSession -ScriptBlock { Get-Disk | Where-Object { $_.SerialNumber -eq $using:StagingDiskSerialNumber } | Set-Disk -IsOffline $True } - -# Verify -Invoke-Command -Session $StagingServerSession -ScriptBlock { Get-Disk | Where-Object { $_.SerialNumber -eq $using:StagingDiskSerialNumber }} - - - -# Snapshot the source CSV -# This example is for an on-demand snapshot. Can adjust code to also use a prior snapshot; ex. regularly scheduled -# snapshots or an asynchronously replicated snapshot from another FlashArray - -# Clone the source CSV to the staging CSV -New-Pfa2Volume -Array $FlashArray -Name $StagingVolumeName -SourceName $SourceVolumeName -Overwrite $true - - - -# Now must resignature the CSV on the staging VM -# Build DISKPART script commands for resignature -$StagingDisk = Invoke-Command -Session $StagingServerSession -ScriptBlock { Get-Disk | Where-Object { $_.SerialNumber -eq $Using:StagingDiskSerialNumber }} -$DiskNumber = $StagingDisk.Number -$NewUniqueID = [GUID]::NewGuid() -$Commands = "`"SELECT DISK $DiskNumber`"", - "`"UNIQUEID DISK ID=$NewUniqueID`"" -$ScriptBlock = [string]::Join(",",$Commands) -$DiskpartScriptBlock = $ExecutionContext.InvokeCommand.NewScriptBlock("$ScriptBlock | DISKPART") - -# Verify DISKPART command -$DiskpartScriptBlock - -# Issue resignature command -Invoke-Command -Session $StagingServerSession -ScriptBlock $DiskpartScriptBlock - - - -# Prepare target VM -$TargetVMSession = New-PSSession -ComputerName $TargetVM -Credential $Credential - -# Offline the database -$Query = "ALTER DATABASE $DatabaseName SET OFFLINE WITH ROLLBACK IMMEDIATE" -Invoke-Command -Session $TargetVMSession -ScriptBlock {Param($querytask) Invoke-Sqlcmd -ServerInstance . -Database master -Query $querytask} -ArgumentList ($Query) - -# Offline the volume -# Because this is a Hyper-V VM, volume serial numbers are not populated by Hyper-V into a virtual machine -# Therefore must use a different method identify the proper volume to offline - -# Confirm which drive you want -Invoke-Command -Session $TargetVMSession -ScriptBlock { Get-Disk | Format-Table } - - - -# Specify the drive number -$DiskNumber = 1 -Invoke-Command -Session $TargetVMSession -ScriptBlock { Get-Disk -Number $using:DiskNumber | Get-Disk | Set-Disk -IsOffline $True } - -# Verify offline -Invoke-Command -Session $TargetVMSession -ScriptBlock { Get-Disk | Format-Table } - - - -# Prepare target VM Host -$TargetVMHostSession = New-PSSession -ComputerName $TargetVMHost -Credential $Credential - -# Remove SQL Server cluster resource dependency on database volume -# Only use this if you are using Clustered Disks (NOT Clustered Shared Volumes) -# Invoke-Command -Session $TargetVMHostSession -ScriptBlock { Get-ClusterResource 'SQL Server' | Remove-ClusterResourceDependency $TargetVolumeName } - -# Stop the disk cluster resource -# NOTE: need to know which Cluster Disk Number first -# This will put the target Hyper-V VM into a Saved state -Invoke-Command -Session $TargetVMHostSession -ScriptBlock { Stop-ClusterResource $Using:TargetClusterDiskNumber } - -# Verify -Invoke-Command -Session $TargetVMHostSession -ScriptBlock { Get-ClusterSharedVolume $Using:TargetClusterDiskNumber } - - - -# Clone the staging CSV to the target CSV -New-Pfa2Volume -Array $FlashArray -Name $TargetVolumeName -SourceName $StagingVolumeName -Overwrite $true - - - -# Start the disk cluster resource -Invoke-Command -Session $TargetVMHostSession -ScriptBlock { Start-ClusterResource $Using:TargetClusterDiskNumber } - -# Verify -Invoke-Command -Session $TargetVMHostSession -ScriptBlock { Get-ClusterSharedVolume $Using:TargetClusterDiskNumber } - - - -# Must now update permissions in Windows to grant the new VM access to the VHDX files - -Invoke-Command -Session $TargetVMHostSession -ScriptBlock { - $VMID = "NT VIRTUAL MACHINE\" - Get-VM -name $Using:TargetVM | Select-Object -ExpandProperty VMID - - $fileAclList = Get-Acl $Using:ClusteredStorageFolder - Foreach ($acl in $fileAclList) { - # Add a new rule to grant full control to a user - $rule = New-Object System.Security.AccessControl.FileSystemAccessRule($VMID, "FullControl", "Allow") - $acl.AddAccessRule($rule) - Set-Acl -Path $acl.PSPath -AclObject $acl - } -} - - - -# Online the volume - -# Confirm which drive you want -Invoke-Command -Session $TargetVMSession -ScriptBlock { Get-Disk | Format-Table } - - - -# Specify the drive number -$DiskNumber = 1 -Invoke-Command -Session $TargetVMSession -ScriptBlock { Get-Disk -Number $using:DiskNumber | Get-Disk | Set-Disk -IsOffline $False } - -# Verify -Invoke-Command -Session $TargetVMSession -ScriptBlock { Get-Disk | Format-Table } - - - -# Online the database -$Query = "ALTER DATABASE $DatabaseName SET ONLINE" -Invoke-Command -Session $TargetVMSession -ScriptBlock {Param($querytask) Invoke-Sqlcmd -ServerInstance . -Database master -Query $querytask} -ArgumentList ($Query) - -# Verify -$Query = "SELECT @@SERVERNAME, name, state_desc, GETDATE() FROM sys.databases WHERE database_id = DB_ID('$DatabaseName')" -Invoke-Command -Session $TargetVMSession -ScriptBlock {Param($querytask) Invoke-Sqlcmd -ServerInstance . -Database master -Query $querytask} -ArgumentList ($Query) - diff --git a/demos-sdk2/Hyper-V CSV w SQL Server Snapshot/HyperV CrashConsistentClone.ps1 b/demos-sdk2/Hyper-V CSV w SQL Server Snapshot/HyperV CrashConsistentClone.ps1 new file mode 100644 index 0000000..eba7478 --- /dev/null +++ b/demos-sdk2/Hyper-V CSV w SQL Server Snapshot/HyperV CrashConsistentClone.ps1 @@ -0,0 +1,225 @@ +############################################################################################################################## +# Pure Storage FlashArray Snapshot Clone - Hyper-V Edition +# Crash-Consistent Snapshot Clone to a Dev/Test SQL Server +# +# Scenario: +# This script will clone a Hyper-V Cluster Shared Volume (CSV), using a crash-consistent snapshot, and present +# it to a second VM running a dev/test SQL Server instance. The source SQL Server continues running uninterrupted +# throughout the entire operation. The target SQL Server performs automatic crash recovery when the database is +# brought online. +# +# Storage topology: +# Source : hyperv-csv-01-DATA-PROD (CSV backing VM1 VHDXs) -> SQL 01 (Production) +# Target : hyperv-csv-01-DATA-DEV (CSV backing VM2 VHDXs) -> SQL 02 (Dev/Test) +# +# This example scenario is useful for refreshing a dev/test SQL Server database from a production FlashArray CSV +# without any downtime on the source. +# +# +# Prerequisites: +# +# FlashArray: +# 1. Source and target volumes must be pre-configured on the FlashArray. +# The target volume must be the same size as the source CSV volume. +# +# Hyper-V Cluster: +# 2. The Failover Cluster PowerShell module must be installed on the Hyper-V node(s). +# Add-WindowsFeature RSAT-Clustering-PowerShell +# 3. PowerShell Remoting (WinRM) must be enabled on all Hyper-V cluster nodes. +# 4. The target VM must have a SCSI controller — hot-add/remove of virtual disks requires SCSI (not IDE). +# +# Modules: +# 5. PureStoragePowerShellSDK2 must be installed on the machine running this script. +# Install-Module PureStoragePowerShellSDK2 +# 6. dbatools must be installed. +# Install-Module dbatools +# +# Credentials: +# 7. Saved credentials file: $HOME\FA_Cred.xml +# $FACred | Export-CliXml -Path "$HOME\FA_Cred.xml" +# 8. $DbName must already exist on the target SQL Server. +# +# +# Usage Notes: +# +# This is a crash-consistent snapshot — SQL Server write IO is NOT frozen before the snapshot is taken. +# The source database remains online and unaffected. The target SQL Server will perform automatic crash +# recovery (roll forward committed transactions, roll back uncommitted) when the database is brought online. +# +# +# Disclaimer: +# This example script is provided AS-IS and meant to be a building block to be adapted to fit an individual +# organization's infrastructure. +############################################################################################################################## + + + +# Import PowerShell modules +Import-Module dbatools +Import-Module PureStoragePowerShellSDK2 + + + +# Initialize variables -- edit these to match your environment +$SourceSQLServer = 'sql-01.example.com' # SQL Server hosting the source database +$TargetSQLServer = 'sql-02.example.com' # SQL Server that will receive the clone +$ArrayName = 'flasharray1.example.com' # FlashArray endpoint +$DbName = 'MyDatabaseName' # Database name + +# Hyper-V cluster details +$HVNode2 = 'hyperv-node-02.example.com' # Hyper-V cluster node that owns the target CSV +$TargetCSVResource = 'Cluster Disk 3' # Cluster resource name for the target CSV + +# FlashArray volume names +$SourceVolName = 'hyperv-csv-01-DATA-PROD' # FA volume backing the source CSV +$TargetVolName = 'hyperv-csv-01-DATA-DEV' # FA volume backing the target CSV + +# VHDX paths on the target CSV (mirrors the source CSV layout after clone) +$ClonedDataVhdx = 'C:\ClusterStorage\Volume3\hyperv-vm-01\hyperv-vm-01-Data.vhdx' +$ClonedLogVhdx = 'C:\ClusterStorage\Volume3\hyperv-vm-01\hyperv-vm-01-Log.vhdx' + +# Target VM name +$TargetVM = 'hyperv-vm-02' +$DataCtrlNum = 0; $DataCtrlLoc = 1 # Data VHDX at SCSI 0:1 +$LogCtrlNum = 0; $LogCtrlLoc = 2 # Log VHDX at SCSI 0:2 + + + +# Build connections +# PowerShell remoting session to the Hyper-V cluster node hosting the target CSV +$HVSession = New-PSSession -ComputerName $HVNode2 + +# SQL connections -- dbatools maintains a persistent SMO connection across cmdlet calls +$SqlInstance1 = Connect-DbaInstance -SqlInstance $SourceSQLServer -TrustServerCertificate -NonPooledConnection +$SqlInstance2 = Connect-DbaInstance -SqlInstance $TargetSQLServer -TrustServerCertificate -NonPooledConnection + +# Connect to the FlashArray's REST API +$FACred = Import-CliXml -Path "$HOME\FA_Cred.xml" +$FlashArray = Connect-Pfa2Array -EndPoint $ArrayName -Credential $FACred -IgnoreCertificateError + + + + +# Let's get some information about the source database on SQL 01; take note of the size +Get-DbaDatabase -SqlInstance $SqlInstance1 -Database $DbName | + Select-Object Name, SizeMB, Status + + + +############################################################################# +# Take a Crash-Consistent Snapshot +############################################################################# + +# Time the full operation from snapshot to database online on SQL 02 +$Start = (Get-Date) + + +# Take a crash-consistent volume snapshot -- SQL Server is running, no freeze +# This is equivalent to pulling the power cord and taking a picture of the disk. +# SQL 02 will perform automatic crash recovery when the database is attached. +$Snapshot = New-Pfa2VolumeSnapshot -Array $FlashArray -SourceName $SourceVolName +$Snapshot + + + +############################################################################# +# Prepare SQL 02 -- Offline Databases and Release File Handles +############################################################################# + +# Offline any user databases on SQL 02 so their file handles are released +$Query = "ALTER DATABASE [$DbName] SET OFFLINE WITH ROLLBACK IMMEDIATE" +Invoke-DbaQuery -SqlInstance $SqlInstance2 -Query $Query + + + +############################################################################# +# Prepare the Hyper-V Storage -- Detach, Clone, Reattach +############################################################################# + +# Detach the data and log VHDXs from the target VM at the hypervisor level +# The CSV must be offlined before overwriting the FlashArray volume, and the +# VHDXs must be detached before the CSV can be taken offline cleanly +Invoke-Command -Session $HVSession -ScriptBlock { + param($vm) + Remove-VMHardDiskDrive -VMName $vm -ControllerType SCSI -ControllerNumber 0 -ControllerLocation 1 + Remove-VMHardDiskDrive -VMName $vm -ControllerType SCSI -ControllerNumber 0 -ControllerLocation 2 + Write-Output "Data VHDX detached (SCSI 0:1)" + Write-Output "Log VHDX detached (SCSI 0:2)" +} -ArgumentList $TargetVM + + +# Take the target CSV offline so the underlying FlashArray volume can be overwritten +Invoke-Command -Session $HVSession -ScriptBlock { + param($res) + Stop-ClusterResource -Name $res -Cluster (Get-Cluster).Name | Out-Null + Write-Output "$res offline" +} -ArgumentList $TargetCSVResource + + +# Clone the snapshot to the target volume -- instantaneous on FlashArray +# New-Pfa2Volume with -Overwrite $true reverts the target volume to the snapshot's contents. +# On a Pure Storage FlashArray this is a metadata operation -- no data is copied. +New-Pfa2Volume -Array $FlashArray -Name $TargetVolName -SourceName $Snapshot.Name -Overwrite $true + + +# Bring the target CSV back online -- the cluster will mount the now-cloned volume +Invoke-Command -Session $HVSession -ScriptBlock { + param($res) + Start-ClusterResource -Name $res -Cluster (Get-Cluster).Name | Out-Null + Write-Output "$res online" +} -ArgumentList $TargetCSVResource + +Start-Sleep -Seconds 5 + + +# Re-attach the cloned VHDXs to the target VM at the same SCSI controller locations +Invoke-Command -Session $HVSession -ScriptBlock { + param($dataVhdx, $logVhdx, $dCN, $dCL, $lCN, $lCL, $vm) + Add-VMHardDiskDrive -VMName $vm -Path $dataVhdx -ControllerType SCSI -ControllerNumber $dCN -ControllerLocation $dCL + Add-VMHardDiskDrive -VMName $vm -Path $logVhdx -ControllerType SCSI -ControllerNumber $lCN -ControllerLocation $lCL + Write-Output "Attached: $dataVhdx (SCSI $($dCN):$($dCL))" + Write-Output "Attached: $logVhdx (SCSI $($lCN):$($lCL))" +} -ArgumentList $ClonedDataVhdx, $ClonedLogVhdx, $DataCtrlNum, $DataCtrlLoc, $LogCtrlNum, $LogCtrlLoc, $TargetVM + + + + +############################################################################# +# Bring the Cloned Database Online on SQL 02 +############################################################################# + +# Wait briefly for CSV and VHDXs to settle +Start-Sleep -Seconds 5 + +# Bring the database online -- SQL Server performs automatic crash recovery +# The database was never cleanly shut down before the snapshot, so SQL Server will +# roll forward the log and roll back any incomplete transactions, just as it would +# after a server restart +$Query = "ALTER DATABASE [$DbName] SET ONLINE" +Invoke-DbaQuery -SqlInstance $SqlInstance2 -Query $Query +Write-Output "$DbName is online on $TargetSQLServer" + + + +############################################################################# +# Verify +############################################################################# + +# Check the database state on SQL 02 -- it should be ONLINE after crash recovery +Get-DbaDbState -SqlInstance $SqlInstance2 -Database $DbName + +# Show all user databases on SQL 02 +Get-DbaDatabase -SqlInstance $SqlInstance2 -Database $DbName | + Select-Object Name, Status, SizeMB + +$Stop = (Get-Date) +Write-Output "Total time from snapshot to database online: $(($Stop - $Start).Seconds) seconds" + + + +############################################################################# +# Cleanup +############################################################################# + +Remove-PSSession $HVSession +Disconnect-Pfa2Array -Array $FlashArray diff --git a/demos-sdk2/Hyper-V CSV w SQL Server Snapshot/HyperV-TSQL-SnapshotBackup.ps1 b/demos-sdk2/Hyper-V CSV w SQL Server Snapshot/HyperV-TSQL-SnapshotBackup.ps1 new file mode 100644 index 0000000..a37e13d --- /dev/null +++ b/demos-sdk2/Hyper-V CSV w SQL Server Snapshot/HyperV-TSQL-SnapshotBackup.ps1 @@ -0,0 +1,276 @@ +############################################################################################################################## +# T-SQL Snapshot Backup and Point-in-Time Recovery - Hyper-V Edition +# +# Scenario: +# This script demonstrates SQL Server 2022 T-SQL Snapshot Backup on a Hyper-V cluster where database files +# reside on VHDXs stored on a Pure Storage FlashArray Cluster Shared Volume (CSV). An application-consistent +# snapshot is taken while SQL Server is running on VM1 (SQL 01), then the cloned volume is presented to VM2 +# (SQL 02) for a point-in-time restore. +# +# Storage topology: +# Source : hyperv-csv-01-DATA-PROD (CSV backing VM1 VHDXs) -> SQL 01 (Production) +# Target : hyperv-csv-01-DATA-DEV (CSV backing VM2 VHDXs) -> SQL 02 (Dev/Test) +# +# This example scenario is useful for seeding a dev/test SQL Server from a production FlashArray CSV with +# a full point-in-time recovery chain. +# +# +# Prerequisites: +# +# FlashArray: +# 1. Source and target volumes must be pre-configured on the FlashArray. +# The target volume must be the same size as the source CSV volume. +# +# Hyper-V Cluster: +# 2. The Failover Cluster PowerShell module must be installed on the Hyper-V node(s). +# Add-WindowsFeature RSAT-Clustering-PowerShell +# 3. PowerShell Remoting (WinRM) must be enabled on all Hyper-V cluster nodes. +# 4. The target VM must have a SCSI controller — hot-add/remove of virtual disks requires SCSI (not IDE). +# +# Modules: +# 5. PureStoragePowerShellSDK2 must be installed on the machine running this script. +# Install-Module PureStoragePowerShellSDK2 +# 6. dbatools must be installed. +# Install-Module dbatools +# +# SQL Server: +# 7. SQL Server 2022 or later is required on both instances for T-SQL Snapshot Backup support. +# 8. $DbName must already exist on the target SQL Server. +# 9. A backup share must be accessible from both SQL 01 and SQL 02 for metadata and log backup files. +# +# Credentials: +# 10. Saved credentials file: $HOME\FA_Cred.xml +# $FACred | Export-CliXml -Path "$HOME\FA_Cred.xml" +# +# +# Usage Notes: +# +# This script uses a volume snapshot (New-Pfa2VolumeSnapshot) rather than a Protection Group snapshot because +# the data and log VHDXs share a single CSV volume — a single volume snapshot captures both files consistently. +# The SUSPEND_FOR_SNAPSHOT_BACKUP and BACKUP METADATA_ONLY commands must share the same SQL Server session; +# this script uses a persistent, non-pooled dbatools connection to satisfy that requirement. +# +# +# Disclaimer: +# This example script is provided AS-IS and meant to be a building block to be adapted to fit an individual +# organization's infrastructure. +############################################################################################################################## + + + +# Import PowerShell modules +Import-Module dbatools +Import-Module PureStoragePowerShellSDK2 + + + +# Initialize variables — edit these to match your environment +$SourceSQLServer = 'sql-01.example.com' # SQL Server hosting the source database +$TargetSQLServer = 'sql-02.example.com' # SQL Server that will receive the clone +$ArrayName = 'flasharray1.example.com' # FlashArray endpoint +$DbName = 'MyDatabaseName' # Database name +$BackupShare = '\\backup-server\BACKUP' # UNC path for metadata and log backups + +# Hyper-V cluster details +$HVNode2 = 'hyperv-node-02.example.com' # Hyper-V cluster node that owns the target CSV +$TargetVM = 'hyperv-vm-02' # VM name receiving the cloned VHDXs +$TargetCSVResource = 'Cluster Disk 3' # Cluster resource name for the target CSV + +# FlashArray volume names +$SourceVolName = 'hyperv-csv-01-DATA-PROD' # FA volume backing the source CSV +$TargetVolName = 'hyperv-csv-01-DATA-DEV' # FA volume backing the target CSV + +# VHDX paths on the target CSV (after clone) +$ClonedDataVhdx = 'C:\ClusterStorage\Volume3\hyperv-vm-01\hyperv-vm-01-Data.vhdx' +$ClonedLogVhdx = 'C:\ClusterStorage\Volume3\hyperv-vm-01\hyperv-vm-01-Log.vhdx' + +# SCSI controller locations for VHDXs on the target VM +$DataCtrlNum = 0; $DataCtrlLoc = 1 # Data VHDX at SCSI 0:1 +$LogCtrlNum = 0; $LogCtrlLoc = 2 # Log VHDX at SCSI 0:2 + + + +# Build connections +# PowerShell remoting session to the Hyper-V cluster node hosting the target CSV +$HVSession = New-PSSession -ComputerName $HVNode2 + +# Persistent, non-pooled SMO connections — required so SUSPEND_FOR_SNAPSHOT_BACKUP +# and BACKUP METADATA_ONLY share the same session (SUSPEND is session-scoped) +$SqlInstance1 = Connect-DbaInstance -SqlInstance $SourceSQLServer -TrustServerCertificate -NonPooledConnection +$SqlInstance2 = Connect-DbaInstance -SqlInstance $TargetSQLServer -TrustServerCertificate -NonPooledConnection + +# Connect to the FlashArray's REST API +$FACred = Import-CliXml -Path "$HOME\FA_Cred.xml" +$FlashArray = Connect-Pfa2Array -EndPoint $ArrayName -Credential $FACred -IgnoreCertificateError + + + + +# Let's get some information about the source database; take note of the size +Get-DbaDatabase -SqlInstance $SqlInstance1 -Database $DbName | + Select-Object Name, SizeMB, Status + + + +############################################################################# +# Take a T-SQL Snapshot Backup +############################################################################# + +# Time the freeze window — this measures how long SQL Server write IO is frozen +$Start = (Get-Date) + + +# Freeze the database for write IO on SQL 01 +$Query = "ALTER DATABASE [$DbName] SET SUSPEND_FOR_SNAPSHOT_BACKUP = ON" +Invoke-DbaQuery -SqlInstance $SqlInstance1 -Query $Query -Verbose + + +# Take a volume snapshot while the database is frozen +$Snapshot = New-Pfa2VolumeSnapshot -Array $FlashArray -SourceName $SourceVolName +$Snapshot + + +# Write the metadata-only backup — this releases the write IO freeze +# MEDIADESCRIPTION stores the snapshot name and array so we can locate the snapshot later +$BackupFile = "$BackupShare\${DbName}_$(Get-Date -Format FileDateTime).bkm" +$Query = "BACKUP DATABASE [$DbName] + TO DISK='$BackupFile' + WITH METADATA_ONLY, + MEDIADESCRIPTION='$($Snapshot.Name)|$($FlashArray.ArrayName)'" +Invoke-DbaQuery -SqlInstance $SqlInstance1 -Query $Query -Verbose + +$Stop = (Get-Date) +Write-Output "The snapshot time takes...$(($Stop - $Start).TotalMilliseconds)ms!" + + +# Check the error log to see what SQL Server thinks happened +Get-DbaErrorLog -SqlInstance $SqlInstance1 -LogNumber 0 | Format-Table + + +# The backup is recorded in MSDB as a Full backup with snapshot +Get-DbaDbBackupHistory -SqlInstance $SqlInstance1 -Database $DbName -Last + + + +############################################################################# +# Take a Log Backup — this extends the restore chain for point-in-time recovery +############################################################################# + +$LogBackup = Backup-DbaDatabase -SqlInstance $SqlInstance1 ` + -Database $DbName ` + -Type Log ` + -Path $BackupShare ` + -CompressBackup + +$LogBackup + + + +############################################################################# +# Point in Time Recovery — Clone the Snapshot to SQL 02 +# +# This is the Hyper-V equivalent of taking the database disk offline, cloning +# the storage snapshot, and bringing the disk back online as described in the +# blog series. Instead of managing a Windows disk serial number, we manage the +# CSV cluster resource and the VHDXs attached to the target Hyper-V VM. +############################################################################# + +# Retrieve the snapshot name from the metadata backup file +# MEDIADESCRIPTION holds the pipe-delimited string we wrote during the backup +$Query = "RESTORE LABELONLY FROM DISK = '$BackupFile'" +$Labels = Invoke-DbaQuery -SqlInstance $SqlInstance2 -Query $Query -Verbose +$SnapshotName = (($Labels | Select-Object MediaDescription -ExpandProperty MediaDescription).Split('|'))[0] +$ArrayName = (($Labels | Select-Object MediaDescription -ExpandProperty MediaDescription).Split('|'))[1] +$SnapshotName +$ArrayName + + +# Offline the database on SQL 02 to release file handles +$Query = "ALTER DATABASE [$DbName] SET OFFLINE WITH ROLLBACK IMMEDIATE" +Invoke-DbaQuery -SqlInstance $SqlInstance2 -Query $Query +$RestoreStart = (Get-Date) + + + +# Detach VHDXs from the target VM before taking the CSV offline +Invoke-Command -Session $HVSession -ScriptBlock { + param($vm) + Remove-VMHardDiskDrive -VMName $vm -ControllerType SCSI -ControllerNumber 0 -ControllerLocation 1 + Remove-VMHardDiskDrive -VMName $vm -ControllerType SCSI -ControllerNumber 0 -ControllerLocation 2 + Write-Output "Data VHDX detached (SCSI 0:1)" + Write-Output "Log VHDX detached (SCSI 0:2)" +} -ArgumentList $TargetVM + + +# Take the target CSV offline so the FlashArray volume can be overwritten +Invoke-Command -Session $HVSession -ScriptBlock { + param($res) + Stop-ClusterResource -Name $res -Cluster (Get-Cluster).Name | Out-Null + Write-Output "$res offline" +} -ArgumentList $TargetCSVResource + + +# Clone the snapshot to the target volume — instantaneous on FlashArray +New-Pfa2Volume -Array $FlashArray -Name $TargetVolName -SourceName $SnapshotName -Overwrite $true + + +# Bring the target CSV back online +Invoke-Command -Session $HVSession -ScriptBlock { + param($res) + Start-ClusterResource -Name $res -Cluster (Get-Cluster).Name | Out-Null + Write-Output "$res online" +} -ArgumentList $TargetCSVResource + +Start-Sleep -Seconds 5 + + +# Re-attach the cloned VHDXs to the target VM +Invoke-Command -Session $HVSession -ScriptBlock { + param($dataVhdx, $logVhdx, $dCN, $dCL, $lCN, $lCL, $vm) + Add-VMHardDiskDrive -VMName $vm -Path $dataVhdx -ControllerType SCSI -ControllerNumber $dCN -ControllerLocation $dCL + Add-VMHardDiskDrive -VMName $vm -Path $logVhdx -ControllerType SCSI -ControllerNumber $lCN -ControllerLocation $lCL + Write-Output "Attached: $dataVhdx (SCSI $($dCN):$($dCL))" + Write-Output "Attached: $logVhdx (SCSI $($lCN):$($lCL))" +} -ArgumentList $ClonedDataVhdx, $ClonedLogVhdx, $DataCtrlNum, $DataCtrlLoc, $LogCtrlNum, $LogCtrlLoc, $TargetVM + + + +# Restore the database from the metadata-only backup file +# METADATA_ONLY tells SQL Server the files are already in place from the snapshot +# NORECOVERY leaves the database in RESTORING mode so we can apply log backups +$Query = "RESTORE DATABASE [$DbName] FROM DISK = '$BackupFile' WITH METADATA_ONLY, REPLACE, NORECOVERY" +Invoke-DbaQuery -SqlInstance $SqlInstance2 -Database master -Query $Query -Verbose + + +# Check the current state of the database — it should be in RESTORING mode +Get-DbaDbState -SqlInstance $SqlInstance2 -Database $DbName + + +# Restore the log backup up to the point in time — database remains in RESTORING mode +$Query = "RESTORE LOG [$DbName] FROM DISK = '$($LogBackup.BackupPath)' WITH NORECOVERY" +Invoke-DbaQuery -SqlInstance $SqlInstance2 -Database master -Query $Query -Verbose + + +# Bring the database online +$Query = "RESTORE DATABASE [$DbName] WITH RECOVERY" +Invoke-DbaQuery -SqlInstance $SqlInstance2 -Database master -Query $Query + +$RestoreStop = (Get-Date) +Write-Output "The restore time takes...$(($RestoreStop - $RestoreStart).TotalMilliseconds)ms!" + + +# Verify the database is online +Get-DbaDbState -SqlInstance $SqlInstance2 -Database $DbName + +Get-DbaDatabase -SqlInstance $SqlInstance2 -Database $DbName | + Select-Object Name, Status, SizeMB + + + + +############################################################################# +# Cleanup +############################################################################# + +Remove-PSSession $HVSession +Disconnect-Pfa2Array -Array $FlashArray diff --git a/demos-sdk2/Hyper-V CSV w SQL Server Snapshot/readme.md b/demos-sdk2/Hyper-V CSV w SQL Server Snapshot/readme.md index e77eb26..d8f6e6f 100644 --- a/demos-sdk2/Hyper-V CSV w SQL Server Snapshot/readme.md +++ b/demos-sdk2/Hyper-V CSV w SQL Server Snapshot/readme.md @@ -1,57 +1,95 @@ -**Hyper-V Cluster Shared Volume + SQL Server Snapshot Scripts** -

-This folder contains Hyper-V Cluster Shared Volume + SQL Server example snapshot scripts. +# Hyper-V Cluster Shared Volume + SQL Server Snapshot Scripts + +This folder contains Hyper-V Cluster Shared Volume (CSV) + SQL Server example snapshot scripts using the Pure Storage FlashArray. **Files:** -- Hyper-V CSV w SQL Server Snapshot.ps1 +- `Hyper-V CSV w SQL Server Snapshot.ps1` - CSV clone with disk resignature; presents the clone back to the originating Hyper-V cluster as a second CSV +- `HyperV CrashConsistentClone.ps1` - crash-consistent snapshot clone from a production CSV to a dev/test SQL Server VM +- `HyperV-TSQL-SnapshotBackup.ps1` - SQL Server 2022 T-SQL Snapshot Backup with point-in-time recovery on a Hyper-V CSV + +--- + +## Hyper-V CSV w SQL Server Snapshot.ps1 + +**Scenario:** + +This script clones a Hyper-V Cluster Shared Volume (CSV) using a crash-consistent snapshot and presents it back to the originating Hyper-V cluster as a second CSV. The target VM spans two CSVs: the first holds the VM OS disk (untouched by this script) and the second holds the SQL Server user database VHDX. Only the database CSV is swapped - the database VHDX is hot-removed, the CSV is overwritten with a fresh clone from the FlashArray, and the VHDX is hot-added back to the running VM. + +**Prerequisites:** + +1. A pre-created target volume on the FlashArray, connected to the target Hyper-V host, must be the same size as the source CSV volume. +2. The Failover Cluster PowerShell module must be installed: `Add-WindowsFeature RSAT-Clustering-PowerShell` +3. PowerShell Remoting (WinRM) must be enabled on the Hyper-V host and inside the target VM guest. +4. The target VM must have a SCSI controller - hot-add/remove requires SCSI (not IDE). +5. The `SqlServer` module must be installed in the guest: `Install-Module SqlServer` +6. The credential used must have Administrator rights on the Hyper-V host, cluster nodes, and the VM guest, plus FlashArray array admin or storage admin role. + +**Important Usage Notes:** + +The clone carries the same disk signature as the source CSV, so it must be resignatured on the target host while offline before it can be used as a CSV. This is handled automatically via DISKPART before the cluster resource is started. + +--- - -
- +## HyperV CrashConsistentClone.ps1 **Scenario:** -
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. - -
-
-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. -
-
-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. -
-
-All references to a "source" refer to the production side (VM, CSV, etc). -All references to a "target" refer to the non-production side (VM, CSV, etc). + +This script clones a production SQL Server Hyper-V CSV to a dev/test SQL Server VM using a crash-consistent FlashArray snapshot. The source SQL Server remains running and unaffected throughout the entire operation. The target SQL Server performs automatic crash recovery (roll forward committed transactions, roll back uncommitted) when the database is brought online - equivalent to recovering after a power loss. + +Storage topology: +- **Source**: `hyperv-csv-01-DATA-PROD` (CSV backing VM1 VHDXs) → SQL 01 (Production) +- **Target**: `hyperv-csv-01-DATA-DEV` (CSV backing VM2 VHDXs) → SQL 02 (Dev/Test) **Prerequisites:** -1. The production CSV must already be cloned and presented once, to the non-production side. -2. This script assumes the database(s) are already attached on the target, non-production SQL Server. + +1. Source and target volumes must be pre-configured on the FlashArray and the same size. +2. The Failover Cluster PowerShell module must be installed: `Add-WindowsFeature RSAT-Clustering-PowerShell` +3. PowerShell Remoting (WinRM) must be enabled on all Hyper-V cluster nodes. +4. The target VM must have a SCSI controller. +5. `PureStoragePowerShellSDK2` and `dbatools` modules must be installed. +6. Saved FlashArray credentials at `$HOME\FA_Cred.xml`. +7. The target database must already exist on the target SQL Server. **Important Usage Notes:** -
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. -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. +This is a crash-consistent snapshot - SQL Server write IO is not frozen before the snapshot is taken. No source downtime or quiescing is required. The VHDXs are detached from the target VM, the target CSV cluster resource is taken offline, the FlashArray volume is overwritten with the snapshot clone (a metadata-only operation on the array), and the CSV and VHDXs are brought back online before the database is recovered. -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). +--- -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. +## HyperV-TSQL-SnapshotBackup.ps1 -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. +**Scenario:** - -
- +This script demonstrates SQL Server 2022 T-SQL Snapshot Backup on a Hyper-V cluster where database files reside on VHDXs stored on a Pure Storage FlashArray CSV. An application-consistent snapshot is taken while SQL Server is running on VM1 (SQL 01). The clone is then presented to VM2 (SQL 02) and restored with a log backup for point-in-time recovery. + +Storage topology: +- **Source**: `hyperv-csv-01-DATA-PROD` (CSV backing VM1 VHDXs) → SQL 01 (Production) +- **Target**: `hyperv-csv-01-DATA-DEV` (CSV backing VM2 VHDXs) → SQL 02 (Dev/Test) + +**Prerequisites:** + +1. Source and target volumes must be pre-configured on the FlashArray and the same size. +2. The Failover Cluster PowerShell module must be installed: `Add-WindowsFeature RSAT-Clustering-PowerShell` +3. PowerShell Remoting (WinRM) must be enabled on all Hyper-V cluster nodes. +4. The target VM must have a SCSI controller. +5. SQL Server 2022 or later is required on both instances for T-SQL Snapshot Backup support. +6. `PureStoragePowerShellSDK2` and `dbatools` modules must be installed. +7. A backup share must be accessible from both SQL 01 and SQL 02 for metadata and log backup files. +8. Saved FlashArray credentials at `$HOME\FA_Cred.xml`. +9. The target database must already exist on the target SQL Server. + +**Important Usage Notes:** + +This script uses a volume snapshot (`New-Pfa2VolumeSnapshot`) rather than a Protection Group snapshot because the data and log VHDXs share a single CSV volume - a single volume snapshot captures both files consistently. The `SUSPEND_FOR_SNAPSHOT_BACKUP` and `BACKUP METADATA_ONLY` commands must share the same SQL Server session; this script uses a persistent, non-pooled dbatools connection (`-NonPooledConnection`) to satisfy that requirement. The snapshot name is embedded in the backup file's `MEDIADESCRIPTION` field so it can be retrieved later during restore. + +--- **Disclaimer:** -
+ This example script is provided AS-IS and meant to be a building block to be adapted to fit an individual organization's infrastructure. -
-
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. - -
- +--- _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._ \ No newline at end of file diff --git a/demos-sdk2/Multi-Array Snapshot/Multi-Array Snapshot.ps1 b/demos-sdk2/Multi-Array Snapshot/Multi-Array Snapshot.ps1 index b3a3e82..802b0dd 100644 --- a/demos-sdk2/Multi-Array Snapshot/Multi-Array Snapshot.ps1 +++ b/demos-sdk2/Multi-Array Snapshot/Multi-Array Snapshot.ps1 @@ -61,8 +61,8 @@ $SqlInstance = Connect-DbaInstance -SqlInstance $TargetSQLServer -TrustServerCer # Connect to the FlashArrays' REST APIs $Credential = Get-Credential -$FlashArray1 = Connect-Pfa2Array –EndPoint $ArrayName1 -Credential $Credential -IgnoreCertificateError -$FlashArray2 = Connect-Pfa2Array –EndPoint $ArrayName2 -Credential $Credential -IgnoreCertificateError +$FlashArray1 = Connect-Pfa2Array -EndPoint $ArrayName1 -Credential $Credential -IgnoreCertificateError +$FlashArray2 = Connect-Pfa2Array -EndPoint $ArrayName2 -Credential $Credential -IgnoreCertificateError diff --git a/demos-sdk2/Point in Time Recovery - VMFS/README.md b/demos-sdk2/Point in Time Recovery - VMFS/README.md deleted file mode 100644 index ecbc7d2..0000000 --- a/demos-sdk2/Point in Time Recovery - VMFS/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# Point In Time Recovery - Using SQL Server 2022's T-SQL Snapshot Backup feature w. VMFS/VMDK datastore/files. - - - -
- - -# Scenario: -Perform a point in time restore using SQL Server 2022's T-SQL Snapshot Backup feature. This uses a FlashArray snapshot as the base of the restore, then restores a log backup. - -# IMPORTANT NOTE: -This example script is built for 1 database spanned across two VMDK files/volumes from a single datastore. - -The granularity or unit of work for this workflow is a VMDK file(s) and the entirety of its contents. Therefore, everything in the VMDK file(s) including files for other databases will be impacted/overwritten. - -This example will need to be adapted if you wish to support multiple databases on the same set of VMDK(s). - -# Prerequisites: -1. PowerShell Modules: dbatools & PureStoragePowerShellSDK2 - -# Usage Notes: -Each section of the script is meant to be run one after the other. The script is not meant to be executed all at once. - - -
- - -# Disclaimer: -This example script is provided AS-IS and is meant to be a building block to be adapted to fit an individual organization's infrastructure. -

-_PLEASE_ do not save your passwords in cleartext here. -Use NTFS secured, encrypted files or whatever else -- never cleartext! -

-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. - - -
- - -_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._ diff --git a/demos-sdk2/Point in Time Recovery/Point in Time Recovery.ps1 b/demos-sdk2/Point in Time Recovery/Point in Time Recovery.ps1 index d900f85..2d57277 100644 --- a/demos-sdk2/Point in Time Recovery/Point in Time Recovery.ps1 +++ b/demos-sdk2/Point in Time Recovery/Point in Time Recovery.ps1 @@ -56,7 +56,7 @@ Get-DbaDatabase -SqlInstance $SqlInstance -Database $DbName | # Connect to the FlashArray's REST API $Credential = Get-Credential -$FlashArray = Connect-Pfa2Array –EndPoint $ArrayName -Credential $Credential -IgnoreCertificateError +$FlashArray = Connect-Pfa2Array -EndPoint $ArrayName -Credential $Credential -IgnoreCertificateError diff --git a/demos-sdk2/Protection Group Database Refresh Between FlashArrays/Protection Group Database Refresh Between FlashArrays.ps1 b/demos-sdk2/Protection Group Database Refresh Between FlashArrays/Protection Group Database Refresh Between FlashArrays.ps1 index d07cba2..455b3fb 100644 --- a/demos-sdk2/Protection Group Database Refresh Between FlashArrays/Protection Group Database Refresh Between FlashArrays.ps1 +++ b/demos-sdk2/Protection Group Database Refresh Between FlashArrays/Protection Group Database Refresh Between FlashArrays.ps1 @@ -53,12 +53,12 @@ $Credential = Get-Credential # Connect to the source FlashArray's REST API -$SourceFlashArray = Connect-Pfa2Array –EndPoint $TargetArrayName -Credential $Credential -IgnoreCertificateError +$SourceFlashArray = Connect-Pfa2Array -EndPoint $SourceArrayName -Credential $Credential -IgnoreCertificateError # Take a snapshot of the Protection Group and replicate it to the target array -$Snapshot = New-Pfa2ProtectionGroupSnapshot -Array $FlashArray -SourceName $ProtectionGroupName -ForReplication $true -ReplicateNow $true +$Snapshot = New-Pfa2ProtectionGroupSnapshot -Array $SourceFlashArray -SourceName $ProtectionGroupName -ForReplication $true -ReplicateNow $true @@ -80,7 +80,7 @@ Invoke-Command -Session $TargetSession -ScriptBlock { Get-Disk | Where-Object { # Connect to the target FlashArray's REST API -$TargetFlashArray = Connect-Pfa2Array –EndPoint $TargetArrayName -Credential $Credential -IgnoreCertificateError +$TargetFlashArray = Connect-Pfa2Array -EndPoint $TargetArrayName -Credential $Credential -IgnoreCertificateError diff --git a/demos-sdk2/Protection Group Database Refresh/Protection Group Database Refresh.ps1 b/demos-sdk2/Protection Group Database Refresh/Protection Group Database Refresh.ps1 index f753830..fdc05e3 100644 --- a/demos-sdk2/Protection Group Database Refresh/Protection Group Database Refresh.ps1 +++ b/demos-sdk2/Protection Group Database Refresh/Protection Group Database Refresh.ps1 @@ -64,7 +64,7 @@ Invoke-Command -Session $TargetSession -ScriptBlock { Get-Disk | Where-Object { # Connect to the FlashArray's REST API -$FlashArray = Connect-Pfa2Array –EndPoint $ArrayName -Credential $Credential -IgnoreCertificateError +$FlashArray = Connect-Pfa2Array -EndPoint $ArrayName -Credential $Credential -IgnoreCertificateError diff --git a/demos-sdk2/Seeding an Availability Group/Seeding an Availability Group.ps1 b/demos-sdk2/Seeding an Availability Group/Seeding an Availability Group.ps1 index 7de7c61..b08312b 100644 --- a/demos-sdk2/Seeding an Availability Group/Seeding an Availability Group.ps1 +++ b/demos-sdk2/Seeding an Availability Group/Seeding an Availability Group.ps1 @@ -63,7 +63,7 @@ $SqlInstanceSecondary = Connect-DbaInstance -SqlInstance $SecondarySqlServer -Tr # Connect to the FlashArray with for the AG Primary $Credential = Get-Credential -$FlashArrayPrimary = Connect-Pfa2Array –EndPoint $PrimaryArrayName -Credential $Credential -IgnoreCertificateError +$FlashArrayPrimary = Connect-Pfa2Array -EndPoint $PrimaryArrayName -Credential $Credential -IgnoreCertificateError @@ -89,7 +89,7 @@ Invoke-DbaQuery -SqlInstance $SqlInstancePrimary -Query $Query -Verbose # Connect to the FlashArray's REST API where the secondary's data is located -$FlashArraySecondary = Connect-Pfa2Array –EndPoint $SecondaryArrayName -Credential $Credential -IgnoreCertificateError +$FlashArraySecondary = Connect-Pfa2Array -EndPoint $SecondaryArrayName -Credential $Credential -IgnoreCertificateError # This is a loop that will block until the snapshot has completed replicating between the two arrays. diff --git a/demos-sdk2/Point in Time Recovery - VMFS/Point in Time Recovery - VMFS.ps1 b/demos-sdk2/VMFS-VMDK Snapshot/Point in Time Recovery - VMFS.ps1 similarity index 98% rename from demos-sdk2/Point in Time Recovery - VMFS/Point in Time Recovery - VMFS.ps1 rename to demos-sdk2/VMFS-VMDK Snapshot/Point in Time Recovery - VMFS.ps1 index e2ad494..e37ad3b 100644 --- a/demos-sdk2/Point in Time Recovery - VMFS/Point in Time Recovery - VMFS.ps1 +++ b/demos-sdk2/VMFS-VMDK Snapshot/Point in Time Recovery - VMFS.ps1 @@ -81,7 +81,7 @@ Get-DbaDatabase -SqlInstance $SqlInstance -Database $DbName | # Connect to the FlashArray's REST API $Credential = Get-Credential -UserName "$env:USERNAME" -Message 'Enter your credential information...' -$FlashArray = Connect-Pfa2Array –EndPoint $ArrayName -Credential $Credential -IgnoreCertificateError +$FlashArray = Connect-Pfa2Array -EndPoint $ArrayName -Credential $Credential -IgnoreCertificateError @@ -306,7 +306,7 @@ Invoke-DbaQuery -SqlInstance $SqlInstance -Database master -Query $Query # Verify Restore -Invoke-DbaQuery -SqlInstance $SqlInstance -Database $DbName -Query "SELECT TOP 10 * FROM dbo.Recipes" +Invoke-DbaQuery -SqlInstance $SqlInstance -Database $DbName -Query "SELECT TOP 10 * FROM Sales.Customer" diff --git a/demos-sdk2/VMFS-VMDK Snapshot/README.md b/demos-sdk2/VMFS-VMDK Snapshot/README.md index 9a459c5..8c26738 100644 --- a/demos-sdk2/VMFS-VMDK Snapshot/README.md +++ b/demos-sdk2/VMFS-VMDK Snapshot/README.md @@ -1,40 +1,62 @@ -# Refresh VMFS VMDK(s) with Snapshot Demo - -
- - -# Scenario: -This example is for a repeatable refresh scenario, such as a nightly refresh of a production database on -another non-production SQL Server. -

-Production SQL Server & database(s) reside on a VMFS datastore. Non-production SQL Server resides on -a different VMFS datastore. User database(s) data and log files reside on two different VMDK disks -in each datastore. This workflow is intended to only be impact select Windows Disks/VMDKs that contain user -databases. Each datastore also resides on a different FlashArray, to demonstrate use of async snapshot -replication. -

-This example's workflow takes an on-demand snapshot of the Production datastore and async replicates it to -the second FlashArray. Then the snapshot is cloned as a new temporary volume/datastore. The VMDKs with the -production database files, residing on the temporary cloned datastore are attached to the target SQL Server, -replacing the prior VMDKs that stored the database files previously. Finally Storage vMotion is used to -migrate the VMDKs to the non-production datastore, then the temporary cloned datastore is discarded. -

- - -# Disclaimer: -This example script is provided AS-IS and is meant to be a building block to be adapted to fit an individual organization's infrastructure. -

-_PLEASE_ do not save your passwords in cleartext here. -Use NTFS secured, encrypted files or whatever else -- never cleartext! -

+# VMFS-VMDK Snapshot Scripts for SQL Server + +This folder contains scripts for managing SQL Server databases on VMware VMFS datastores using Pure Storage FlashArray snapshots. + +**Files:** +- `VMFS-VMDK Snapshot.ps1` — repeatable database refresh from a FlashArray snapshot across two VMFS datastores +- `Point in Time Recovery - VMFS.ps1` — SQL Server 2022 T-SQL Snapshot Backup with point-in-time recovery on VMFS/VMDK + +--- + +## VMFS-VMDK Snapshot.ps1 + +**Scenario:** + +This script is for a repeatable refresh scenario, such as a nightly refresh of a production database onto a non-production SQL Server. Production SQL Server databases reside on a VMFS datastore on one FlashArray. The non-production SQL Server resides on a different VMFS datastore on a second FlashArray. + +The workflow takes an on-demand snapshot of the production datastore and async replicates it to the second FlashArray. The snapshot is then cloned as a new temporary volume/datastore. The VMDKs containing the production database files are attached to the target SQL Server, replacing the prior VMDKs. Finally, Storage vMotion migrates the VMDKs to the non-production datastore and the temporary cloned datastore is discarded. + +**Prerequisites:** + +1. PowerShell Modules: `dbatools` & `PureStoragePowerShellSDK2` +2. VMware PowerCLI must be installed. +3. Async replication must be configured between the source and target FlashArrays. + +--- + +## Point in Time Recovery - VMFS.ps1 + +**Scenario:** + +This script performs a point-in-time restore using SQL Server 2022's T-SQL Snapshot Backup feature with a FlashArray snapshot as the base, followed by restoring a native SQL Server log backup. + +**Important Note:** + +This script is built for a single database spanned across two VMDK files from a single datastore. The granularity of this workflow is a VMDK file and the entirety of its contents — everything in the VMDK, including files for other databases, will be impacted and overwritten. This script will need to be adapted to support multiple databases on the same VMDK(s). + +**Prerequisites:** + +1. PowerShell Modules: `dbatools` & `PureStoragePowerShellSDK2` +2. VMware PowerCLI must be installed. +3. SQL Server 2022 or later is required for T-SQL Snapshot Backup support. + +**Usage Notes:** + +Each section of the script is meant to be run one after the other. The script is not meant to be executed all at once. + +--- + +## Disclaimer + +This example script is provided AS-IS and is meant to be a building block to be adapted to fit an individual organization's infrastructure. + 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. - -
- +--- _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._ + diff --git a/demos-sdk2/Volume Database Refresh/Volume Database Refresh.ps1 b/demos-sdk2/Volume Database Refresh/Volume Database Refresh.ps1 index 0d4ac4f..9af5441 100644 --- a/demos-sdk2/Volume Database Refresh/Volume Database Refresh.ps1 +++ b/demos-sdk2/Volume Database Refresh/Volume Database Refresh.ps1 @@ -60,7 +60,7 @@ Invoke-Command -Session $TargetSession -ScriptBlock { Get-Disk | Where-Object { # Connect to the FlashArray's REST API -$FlashArray = Connect-Pfa2Array –EndPoint $ArrayName -Credential $Credential -IgnoreCertificateError +$FlashArray = Connect-Pfa2Array -EndPoint $ArrayName -Credential $Credential -IgnoreCertificateError