|
| 1 | +############################################################################################################################## |
| 2 | +# Refresh VMFS VMDK with Snapshot Demo |
| 3 | +# |
| 4 | +# |
| 5 | +# Scenario: |
| 6 | +# Snapshot and clone a "production" VMDK in a VMFS datastore, then present it to a "non-production" server. |
| 7 | +# |
| 8 | +# This example has two databases: ExampleDb1, ExampleDb2, whose data and log files both reside on a single disk/VMDK. |
| 9 | +# |
| 10 | +# |
| 11 | +# Usage Notes: |
| 12 | +# |
| 13 | +# You must pre-setup the target VM with a cloned datastore from the source already. You will ONLY be utilizing |
| 14 | +# the SPECIFIC VMDK(s) that contain the data/log files of interest, from the cloned datastore. Other VMDKs can safely be |
| 15 | +# ignored since they are deduped on FlashArray. |
| 16 | +# |
| 17 | +# For the cloned datastore pre-setup, you can use subsets of the code below to clone the source datastore, present it to |
| 18 | +# the target server, then attach the VMDK(s) containing the production databases that will be re-cloned with this script. |
| 19 | +# Once "staged," you can then use this script fully to refresh the data files in the cloned datastore that is attached |
| 20 | +# to the target server. |
| 21 | +# |
| 22 | +# This script also assumes that all database files (data and log) are on the same volume/single VMDK. If multiple |
| 23 | +# volumes/VMDKs are being used, adjust the code to add additional foreach loops when manipulating the VMDKs. |
| 24 | +# |
| 25 | +# 2025/12/22: AYun - Renamed to "VMFS-VMDK Snapshot - V1.ps1" and migrated to archive in |
| 26 | +# PureStorage-OpenConnect\sqlserver-scripts\demos-archive\VMFS-VMDK Snapshot |
| 27 | +# |
| 28 | +# Disclaimer: |
| 29 | +# This example script is provided AS-IS and meant to be a building block to be adapted to fit an individual |
| 30 | +# organization's infrastructure. |
| 31 | +############################################################################################################################## |
| 32 | + |
| 33 | + |
| 34 | + |
| 35 | +# Import powershell modules |
| 36 | +Import-Module PureStoragePowerShellSDK2 |
| 37 | +Import-Module VMware.VimAutomation.Core |
| 38 | +Import-Module SqlServer |
| 39 | + |
| 40 | + |
| 41 | + |
| 42 | +# Declare variables |
| 43 | +$TargetVM = 'SqlServer1' # Name of target VM |
| 44 | +$Databases = @('ExampleDb1','ExampleDb2') # Array of database names |
| 45 | +$TargetDiskSerialNumber = '6000c02022cb876dcd321example01b' # Target Disk Serial Number |
| 46 | +$VIServerName = 'vcenter.example.com' # vCenter FQDN |
| 47 | +$ClusterName = 'WorkloadCluster1' # VMware Cluster |
| 48 | +$SourceDatastoreName = 'vmware_sql_datastore' # VMware datastore name |
| 49 | +$SourceVMDKPath = 'SqlServer1_1/SqlServer1.vmdk' # VMDK path inside the VMFS datastore |
| 50 | +$ArrayName = 'flasharray1.example.com' # FlashArray FQDN |
| 51 | +$SourceVolumeName = 'sql_volume_1' # Source volume name on FlashArray (may be same as your datastore name) |
| 52 | +$TargetVolumeName = 'sql_volume_2' # Target volume name on FlashArray (may be same as your datastore name) |
| 53 | + |
| 54 | + |
| 55 | + |
| 56 | +# Set Credential - this assumes the same credential for the target VM and vCenter |
| 57 | +$Credential = Get-Credential |
| 58 | + |
| 59 | + |
| 60 | + |
| 61 | +# Create a Powershell session against the target VM |
| 62 | +$TargetVMSession = New-PSSession -ComputerName $TargetVM -Credential $Credential |
| 63 | + |
| 64 | + |
| 65 | + |
| 66 | +# Connect to vCenter |
| 67 | +$VIServer = Connect-VIServer -Server $VIServerName -Protocol https -Credential $Credential |
| 68 | + |
| 69 | + |
| 70 | + |
| 71 | +# Offline the target database(s) by looping through $Databases array |
| 72 | +foreach ($Database in $Databases) { |
| 73 | + $Query = "ALTER DATABASE [$Database] SET OFFLINE WITH ROLLBACK IMMEDIATE" |
| 74 | + Invoke-Sqlcmd -ServerInstance $TargetVM -Database master -Query $Query |
| 75 | +} |
| 76 | + |
| 77 | + |
| 78 | + |
| 79 | +# Offline the volumes that have SQL data |
| 80 | +Invoke-Command -Session $TargetVMSession -ScriptBlock { Get-Disk | Where-Object { $_.SerialNumber -eq $using:TargetDiskSerialNumber } | Set-Disk -IsOffline $True } |
| 81 | + |
| 82 | + |
| 83 | + |
| 84 | +# Prepare to remove the VMDK from the VM |
| 85 | +$VM = Get-VM -Server $VIServer -Name $TargetVM |
| 86 | +$HardDisk = Get-HardDisk -VM $VM | Where-Object { $_.FileName -match $SourceVMDKPath } |
| 87 | + |
| 88 | + |
| 89 | + |
| 90 | +# Remove the VMDK from the VM |
| 91 | +Remove-HardDisk -HardDisk $HardDisk -Confirm:$false |
| 92 | + |
| 93 | + |
| 94 | + |
| 95 | +# Prepare to remove the stale datastore |
| 96 | +$DataStore = $HardDisk.Filename.Substring(1, ($HardDisk.Filename.LastIndexOf(']') - 1)) |
| 97 | +$Hosts = Get-Cluster $ClusterName | Get-VMHost | Where-Object { ($_.ConnectionState -eq 'Connected') } |
| 98 | + |
| 99 | + |
| 100 | + |
| 101 | +# Guest hard disk removed, now remove the stale datastore - this can take a min or two |
| 102 | +Get-Datastore $DataStore | Remove-Datastore -VMHost $Hosts[0] -Confirm:$False |
| 103 | + |
| 104 | + |
| 105 | + |
| 106 | +# Connect to the array, authenticate. Remember disclaimer at the top! |
| 107 | +$FlashArray = Connect-Pfa2Array -Endpoint $ArrayName -Credential ($Credential) -IgnoreCertificateError |
| 108 | + |
| 109 | + |
| 110 | + |
| 111 | +# Perform the volume overwrite (no intermediate snapshot needed!) |
| 112 | +New-Pfa2Volume -Array $FlashArray -Name $TargetVolumeName -SourceName $SourceVolumeName -Overwrite $True |
| 113 | + |
| 114 | + |
| 115 | + |
| 116 | +# Rescan storage on each ESX host in the $Hosts array |
| 117 | +foreach ($VmHost in $Hosts) { |
| 118 | + Get-VMHostStorage -RescanAllHba -RescanVmfs -VMHost $VmHost | Out-Null |
| 119 | +} |
| 120 | + |
| 121 | + |
| 122 | + |
| 123 | +# Connect to EsxCli |
| 124 | +$esxcli = Get-EsxCli -VMHost $Hosts[0] |
| 125 | + |
| 126 | + |
| 127 | + |
| 128 | +# Resignature the cloned datastore |
| 129 | +$EsxCli.Storage.Vmfs.Snapshot.Resignature($SourceDatastoreName) |
| 130 | + |
| 131 | + |
| 132 | + |
| 133 | +# Find the assigned datastore name, this may take a few seconds |
| 134 | +# NOTE: when a datastore comes back, it's name will be "snap-[GUID chars]-[original DS name]" |
| 135 | +# This is why the wildcard match below is needed. |
| 136 | +$DataStore = (Get-Datastore | Where-Object { $_.Name -match 'snap' -and $_.Name -match $SourceDatastoreName }) |
| 137 | + |
| 138 | + |
| 139 | + |
| 140 | +# Rescan storage again to make sure all hosts can see the new datastore |
| 141 | +foreach ($VmHost in $Hosts) { |
| 142 | + Get-VMHostStorage -RescanAllHba -RescanVmfs -VMHost $VmHost | Out-Null |
| 143 | +} |
| 144 | + |
| 145 | + |
| 146 | + |
| 147 | +# Attach the VMDK from the newly cloned datastore back to the target VM |
| 148 | +New-HardDisk -VM $VM -DiskPath "[$DataStore] $SourceVMDKPath" |
| 149 | + |
| 150 | + |
| 151 | + |
| 152 | +# Online the volume on the target VM |
| 153 | +Invoke-Command -Session $TargetVMSession -ScriptBlock { Get-Disk | Where-Object { $_.SerialNumber -eq $using:TargetDiskSerialNumber } | Set-Disk -IsOffline $False } |
| 154 | + |
| 155 | + |
| 156 | + |
| 157 | +# Volume might be read-only, ensure it's read/write |
| 158 | +Invoke-Command -Session $TargetVMSession -ScriptBlock { Get-Disk | Where-Object { $_.SerialNumber -eq $using:TargetDiskSerialNumber } | Set-Disk -IsReadOnly $False } |
| 159 | + |
| 160 | + |
| 161 | + |
| 162 | +# Online the target database(s) by looping through $Databases array |
| 163 | +foreach ($Database in $Databases) { |
| 164 | + $Query = "ALTER DATABASE [$Database] SET ONLINE WITH ROLLBACK IMMEDIATE" |
| 165 | + Invoke-Sqlcmd -ServerInstance $TargetVM -Database master -Query $Query |
| 166 | +} |
| 167 | + |
| 168 | + |
| 169 | + |
| 170 | +# Remove powershell session |
| 171 | +Remove-PSSession $TargetVMSession |
0 commit comments