Crash-Consistent Snapshot Cloning - Hyper-V Edition

Page content

If you’ve been following my T-SQL Snapshot Backup series, most of what I’ve covered requires SQL Server to participate in the snapshot: the write IO freeze, the metadata backup, the coordinated workflow. This post covers the other side of that coin: crash-consistent cloning. No write freeze. No backup. No point-in-time recovery. Just a raw volume clone that SQL Server recovers from automatically when you attach it.

What Is a Crash-Consistent Snapshot?

When people talk about snapshot backups for SQL Server, they often jump straight to application-consistent snapshots, the kind where SQL Server is asked to freeze write IO before the snapshot. That freeze guarantees every page on disk reflects a logically consistent database state.

A crash-consistent snapshot is different. We take a picture of the storage at a point in time without asking SQL Server’s permission. No freeze. No quiesce. The database files on disk at the moment of the snapshot are in exactly the same state as if someone had pulled the power cord.

So why would you ever want that? Because SQL Server already knows how to recover from a power loss. That’s what crash recovery is for. Every time SQL Server starts up after an unclean shutdown, it reads the transaction log, rolls forward all committed transactions that weren’t flushed to the data files, and rolls back anything that was in-flight when the crash happened. When we attach a crash-consistent snapshot clone, SQL Server runs that same process automatically. You just attach or online the database and it takes care of the rest.

Crash-consistent is not a backup strategy for production restore. The recovered database will be transactionally and physcially consistent, but you can only recover to the instant of the snapshot. For production PITR you want an application-consistent snapshot backup with a log chain, which is what the other posts in T-SQL Snapshot Backup blogs series cover.

Where crash-consistent really shines is dev/test or reporting database refresh: you want the latest production data, you don’t care about losing the last few minutes, and you want it done as fast as possible with zero impact on the source SQL Server. I tell our customers, if you’re using backup and restore to move data between systems, stop doing that and start doing this.

That’s exactly what this script does.

The Storage Topology

Before we get into the code, let’s understand the environment. In this lab I have a two-node Hyper-V cluster. SQL Server runs inside a VM on each node. The database files for TPCC-4T live on two VHDXs stored on a Pure Storage FlashArray Cluster Shared Volume named hyperv-csv-01-DATA-PROD.

Here’s what the storage stack looks like:

FlashArray Volume (hyperv-csv-01-DATA-PROD)
    └── Cluster Shared Volume (CSV) -> C:\ClusterStorage\Volume10 on HV Node 1
            └── VHDX files
                    ├── hv-sql-01-Data.vhdx  -> presented to hv-sql-01 as D: (data)
                    └── hv-sql-01-Log.vhdx   -> presented to hv-sql-01 as L: (log)
                            └── SQL Server: TPCC-4T

The restore target is hv-sql-02, which has its own CSV (Cluster Disk 11) backed by a separate FlashArray volume named(hyperv-csv-01-DATA-DEV). We’re going to overwrite that target volume with a clone of the production snapshot, and then attach the database on SQL 02.

One thing worth noting: both the data VHDX and the log VHDX live on the same FlashArray volume which is also the same CSV. A single volume snapshot captures both files in the same point-in-time picture. That’s why we use New-Pfa2VolumeSnapshot on the volume directly, rather than a Protection Group snapshot covering multiple volumes. If your VHDX files are in separate CSVs on separate FlashArray volumes they MUST be put into a Protection Group for this to work properly.

Initialize the Script’s Variables and Connections

To begin, let’s set up the PowerShell variables. I’m using dbatools and the Pure Storage PowerShell SDK2.

Import-Module dbatools
Import-Module PureStoragePowerShellSDK2

$SourceSQLServer   = 'hv-sql-01'                          # SQL Server hosting the source database
$TargetSQLServer   = 'hv-sql-02'                          # SQL Server that will receive the clone
$ArrayName         = 'flasharray1.fsa.lab'                # FlashArray endpoint
$DbName            = 'TPCC-4T'                            # Database name

$HVNode2           = 'sql-fci-02.fsa.lab'                 # Hyper-V cluster node that owns the target CSV
$TargetCSVResource = 'Cluster Disk 11'                    # Cluster resource name for the target CSV

$SourceVolName     = 'hyperv-csv-01-DATA-PROD'            # FA volume backing the source CSV
$TargetVolName     = 'hyperv-csv-01-DATA-DEV'             # FA volume backing the target CSV

$ClonedDataVhdx    = 'C:\ClusterStorage\Volume11\hv-sql-01\hv-sql-01-Data.vhdx'
$ClonedLogVhdx     = 'C:\ClusterStorage\Volume11\hv-sql-01\hv-sql-01-Log.vhdx'

$DataCtrlNum = 0; $DataCtrlLoc = 1                        # Data VHDX at SCSI 0:1
$LogCtrlNum  = 0; $LogCtrlLoc  = 2                        # Log  VHDX at SCSI 0:2

Build Connections

I need three connections: a PowerShell remoting session to the Hyper-V cluster node, connections to both SQL Server instances, and a session to the FlashArray REST API. You don’t have to use dbatools to connect to SQL Server, but I recommend it…makes things easier.

$HVSession = New-PSSession -ComputerName $HVNode2

$SqlInstance1 = Connect-DbaInstance -SqlInstance $SourceSQLServer -TrustServerCertificate -NonPooledConnection
$SqlInstance2 = Connect-DbaInstance -SqlInstance $TargetSQLServer -TrustServerCertificate -NonPooledConnection

$FACred     = Import-CliXml -Path "$HOME\FA_Cred.xml"
$FlashArray = Connect-Pfa2Array -EndPoint $ArrayName -Credential $FACred -IgnoreCertificateError

The $HVSession is the remoting session to the Hyper-V host node, and we’ll use it to manage CSV cluster resources and VHDX attachments at the hypervisor level.

Let’s get a quick look at the source database before we do anything.

Get-DbaDatabase -SqlInstance $SqlInstance1 -Database $DbName |
    Select-Object Name, SizeMB, Status

Name     SizeMB      Status
----     ------      ------
TPCC-4T  4043579.50  Normal

Taking the Crash-Consistent Snapshot

Now let’s take the snapshot. The source SQL Server keeps running with zero interruption. We’re not asking it to do anything.

Note on Hyper-V CSV cache: If the CSV I/O write cache is enabled on your Hyper-V cluster, writes can sit in host memory before being flushed to the FlashArray volume. A FlashArray snapshot captures what’s on the volume, not what’s in the cache. If CSV caching is disabled (which is a best practice when using high-performance storage like FlashArray), this isn’t a concern. If it’s enabled, issue a Checkpoint-VM (Production Checkpoint) before the snapshot to flush the cache first.

$Start = (Get-Date)

$Snapshot = New-Pfa2VolumeSnapshot -Array $FlashArray -SourceName $SourceVolName
$Snapshot

Id      : 9c7ab79a-afbd-0279-2fcf-3648e03a2647
Name    : hyperv-csv-01-DATA-PROD.17
Created : 5/8/2026 5:04:41 PM
Destroyed : False
Source  : @{Id='73cc51e1-b7a7-6000-d3f1-f3e4b19fd030'; Name='hyperv-csv-01-DATA-PROD'}
Suffix  : 17

New-Pfa2VolumeSnapshot is instantaneous from the array’s perspective. The snapshot name comes back with a suffix (.17 here) appended to the source volume name. Both VHDXs on this CSV are captured in the same atomic operation.

I set $Start before the snapshot fires so the timing includes the full storage restore sequence that follows.

Preparing SQL 02 for the Clone

Before we can touch the storage, we need to get SQL Server out of the way on the target side. SQL Server holds open file handles to the VHDX-backed volumes. We don’t need to shut SQL Server down. We just need to offline the user databases so their file handles are released. Keep in mind, if you were doing this with backup/restore the databases would be unavailable for the duration of the restore.

$Query = "ALTER DATABASE [$DbName] SET OFFLINE WITH ROLLBACK IMMEDIATE"
Invoke-DbaQuery -SqlInstance $SqlInstance2 -Query $Query

Only the user databases I want to update will need go offline. System databases (master, msdb, model, tempdb) are untouched.

The Hyper-V Storage Sequence

This is the part that’s different from a bare-metal snapshot restore. On bare metal, the storage swap is just “offline the disk, clone, online the disk.” In a Hyper-V environment with a CSV there are more layers: the VHDX files are attached to a VM at the hypervisor level, and the CSV is a cluster resource. We need to unwind those layers before we can swap the volume, then rebuild them afterward.

Here’s the sequence:

  1. Detach the VHDXs from the target VM
  2. Offline the CSV cluster resource
  3. Clone the snapshot to the target volume
  4. Online the CSV cluster resource
  5. Re-attach the VHDXs to the target VM

Detach the VHDXs from the Target VM

We run this through $HVSession, the remoting session to the Hyper-V host node. We’re basically unplugging the virtual hard drives from the VM because we’re about to update their contents from the snapshot. Keep in mind, that any files on these VHDXs are unavailable during this time. So when designing your database file topology, plan accordingly.

Invoke-Command -Session $HVSession -ScriptBlock {
    Remove-VMHardDiskDrive -VMName 'hv-sql-02' -ControllerType SCSI -ControllerNumber 0 -ControllerLocation 1
    Remove-VMHardDiskDrive -VMName 'hv-sql-02' -ControllerType SCSI -ControllerNumber 0 -ControllerLocation 2
}

Offline the CSV

The FlashArray volume can’t be overwritten while the cluster has it mounted as an active CSV. Stop-ClusterResource takes it offline cleanly. Keep in mind, anything that’s shared in this CSV will be unavailable during this time. So when designing your storage topology, plan accordingly.

Invoke-Command -Session $HVSession -ScriptBlock {
    param($res)
    Stop-ClusterResource -Name $res -Cluster (Get-Cluster).Name | Out-Null
    Write-Output "$res offline"
} -ArgumentList $TargetCSVResource

Cluster Disk 11 offline

Clone the Snapshot to the Target Volume

This is the moment that does the actual work, and it’s nearly instant. New-Pfa2Volume with -Overwrite $true overwrites the target volume to the exact state captured in the snapshot. On a Pure Storage FlashArray, this is a metadata-only operation: no data is physically copied, regardless of how large the volume is. A 10 TB volume reverts in the same time as a 10 GB volume and as a 10KB volume.

New-Pfa2Volume -Array $FlashArray -Name $TargetVolName -SourceName $Snapshot.Name -Overwrite $true

Id      : d7e39f69-fefc-8578-5a9f-cc5b87981952
Name    : hyperv-csv-01-DATA-DEV
Source  : @{Id='73cc51e1-b7a7-6000-d3f1-f3e4b19fd030'; Name='hyperv-csv-01-DATA-PROD'}

Online the CSV and Re-attach the VHDXs

Bring the cluster resource back online. The cluster will remount 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

Cluster Disk 11 online

Wait a few seconds for the CSV to mount, then re-attach the VHDXs at the same SCSI controller locations.

Start-Sleep -Seconds 5

Invoke-Command -Session $HVSession -ScriptBlock {
    param($dataVhdx, $logVhdx, $dCN, $dCL, $lCN, $lCL)
    Add-VMHardDiskDrive -VMName 'hv-sql-02' -Path $dataVhdx -ControllerType SCSI -ControllerNumber $dCN -ControllerLocation $dCL
    Add-VMHardDiskDrive -VMName 'hv-sql-02' -Path $logVhdx  -ControllerType SCSI -ControllerNumber $lCN -ControllerLocation $lCL
} -ArgumentList $ClonedDataVhdx, $ClonedLogVhdx, $DataCtrlNum, $DataCtrlLoc, $LogCtrlNum, $LogCtrlLoc

Bringing the Database Online on SQL 02

With the storage in place, we just need to bring the database back online. We set it offline at the start of the script to release file handles before the storage swap. Now that the cloned volume is mounted and the VHDXs are re-attached, SET ONLINE is all it takes.

$Query = "ALTER DATABASE [$DbName] SET ONLINE"
Invoke-DbaQuery -SqlInstance $SqlInstance2 -Query $Query
Write-Output "$DbName is online on $TargetSQLServer"

TPCC-4T is online on hv-sql-02

When SQL Server brings the database online, it detects that the files reflect an unplanned shutdown. It automatically runs crash recovery: rolls forward committed transactions from the log, rolls back anything that was in-flight, and brings the database online. You don’t issue any RESTORE command. You don’t write a log backup. SQL Server just handles it.

Verifying the Final State

Get-DbaDbState -SqlInstance $SqlInstance2 -Database $DbName

Access       : MULTI_USER
ComputerName : hv-sql-02
DatabaseName : TPCC-4T
InstanceName : MSSQLSERVER
RW           : READ_WRITE
SqlInstance  : hv-sql-02
Status       : ONLINE

Get-DbaDatabase -SqlInstance $SqlInstance2 -Database $DbName | Select-Object Name, Status, SizeMB

Name     SizeMB      Status
----     ------      ------
TPCC-4T  4043579.50  Normal

TPCC-4T is online, read-write, and showing the correct size on hv-sql-02.

How Long Did That Take?

$Stop = (Get-Date)
Write-Output "Total time from snapshot to database online: $(($Stop - $Start).Seconds) seconds"

Total time from snapshot to database online: 25 seconds

25 seconds from snapshot to a live, fully-recovered cloned database on the dev/test SQL Server. The source SQL Server ran without interruption throughout the entire process. This is what snapshot-based database refresh looks like when the storage does the heavy lifting.

Cleanup

Remove-PSSession $HVSession
Disconnect-Pfa2Array -Array $FlashArray
Write-Output "Session and FlashArray connection closed."

Session and FlashArray connection closed.

Summary

Here’s what happened in those 25 seconds:

Step Time
Crash-consistent volume snapshot ~instant
Offline user DBs on SQL 02 ~1s
Detach VHDXs from VM ~2s
Offline CSV cluster resource ~2s
Clone snapshot to target volume (metadata operation) ~instant
Online CSV cluster resource ~3s
Re-attach VHDXs to VM ~5s
ALTER DATABASE SET ONLINE + crash recovery ~5s

The FlashArray clone itself is effectively zero time: it’s a metadata pointer swap. The rest of the elapsed time is Windows and Hyper-V cluster operations: mounting the CSV, presenting the virtual disks to the guest, and SQL Server’s crash recovery pass on the log.

Compare that to a traditional backup-restore refresh cycle: dump a full backup across the network, restore it to the target server. For a multi-terabyte database that might be hours. With a snapshot clone it’s always measured in seconds, regardless of database size.

When Should You Use Crash-Consistent vs. Application-Consistent?

Crash-Consistent Application-Consistent (T-SQL Snapshot Backup)
Source SQL impact None Sub-second write freeze
Snapshot type Storage-level only Storage-level + SQL Server metadata backup
PITR capability No Yes (with log backup chain)
Restore process SET ONLINE + auto crash recovery RESTORE WITH METADATA_ONLY + RESTORE LOG
Best for Dev/test or reporting refresh, cloning Production backups, PITR

If you need point-in-time recovery or you want a proper SQL Server backup entry in msdb that plays nicely with your existing restore automation, use the T-SQL Snapshot Backup approach covered in the rest of this series. If you just need the fastest possible database refresh to a dev/test or reporting server and you don’t need PITR, crash-consistent is the right tool for the job.

Wrapping Up

Crash-consistent snapshot cloning gets you from production data to a refreshed dev/test or reportingdatabase in under a minute, with zero impact on the source SQL Server. The FlashArray clone itself is a metadata pointer swap, effectively zero time, regardless of database size. The rest of the elapsed time is Windows and Hyper-V cluster operations.

The key things to take away from this post:

  • Crash-consistent requires zero SQL Server coordination - no freeze, no write IO interruption on the source server
  • Both VHDXs are captured atomically - because the data and log files share a single FlashArray volume, one New-Pfa2VolumeSnapshot call captures them in a consistent state
  • If you need PITR - look at the T-SQL Snapshot Backup posts in this series. The Using T-SQL Snapshot Backup - Hyper-V Edition covers the full application-consistent workflow with SUSPEND_FOR_SNAPSHOT_BACKUP and point-in-time log restore

Get out in your lab and try it. Let me know how it works in your environment.

T-SQL Snapshot Backup Series

This post is part of my ongoing series on T-SQL Snapshot Backups. Here are all the posts in the series:

  1. Using T-SQL Snapshot Backup - Are Snapshots Backups? - Introduction to T-SQL Snapshot Backup fundamentals and the theory behind using snapshots as backups
  2. Understanding I/O Freeze in T-SQL Snapshot Backups - Understand the I/O freeze mechanism that enables T-SQL Snapshot Backups and how SQL Server suspends write I/O to ensure consistent snapshots
  3. Using T-SQL Snapshot Backup - Point in Time Recovery - Learn how to perform point-in-time recovery using snapshot backups and transaction log backups
  4. Using T-SQL Snapshot Backup - Point in Time Recovery - Azure Edition - Implementing T-SQL Snapshot Backup techniques with Azure Virtual Disks
  5. Using T-SQL Snapshot Backup - Multi-Array Database Snapshots - Coordinating snapshots across multiple Pure Storage FlashArrays for distributed databases
  6. Using T-SQL Snapshot Backup - Seeding Availability Groups - Use snapshot backups to quickly seed new Availability Group replicas
  7. T-SQL REST API Integration in SQL Server 2025: Streamlining T-SQL Snapshot Backups - Leverage SQL Server 2025’s native REST API capabilities for direct storage integration
  8. Building a Snapshot Backup Catalog - Create a queryable snapshot catalog using SQL Server 2025’s native REST API integration
  9. Building a Snapshot Backup Catalog - SQL Server 2022 Edition - Achieve the same snapshot catalog functionality using PowerShell with SQL Server 2022
  10. Using T-SQL Snapshot Backup - Hyper-V Edition - T-SQL Snapshot Backup in a Hyper-V cluster environment with CSV-backed VHDXs on Pure Storage FlashArray
  11. Crash-Consistent Snapshot Cloning - Hyper-V Edition (This Post) - Crash-consistent snapshot cloning for dev/test database refresh in a Hyper-V environment