Skip to content

Commit 021d4cd

Browse files
committed
New-StrechedFileCluster.ps1 Added
1 parent e9b4399 commit 021d4cd

File tree

2 files changed

+272
-0
lines changed

2 files changed

+272
-0
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
11
# PowerShell
22
Personal PowerShell Script collection
3+
4+
## Content:
5+
* **StorageReplica/New-StrechedFileCluster.ps1:**
6+
Simple Script to build a virutal two node, streched, general purpose file server cluster with storage replica
7+
Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
<#
2+
.SYNOPSIS
3+
Builds a virutal two node, streched, general purpose file server cluster with storage replica
4+
.DESCRIPTION
5+
Script with all steps to build a streched cluster, based on two VMs, with a general purpose file server cluster role.
6+
The underneath storage is not shared storage but local storage. The volume insiede the VMs will then replicated with Storage Replica
7+
8+
The sicript is intended to run on a third computer like a managemnt server where the Failover Cluster, Hyper-V and Storage Replica RSAT tools are installed.
9+
10+
.PARAMETER Server
11+
Computer names of the thwo cluster nodes
12+
13+
.PARAMETER Locations
14+
Name of the two sites. Will be used to name the sites in cluster fault domains configuration
15+
16+
.PARAMETER ClusterName
17+
Name of the failover cluster
18+
19+
.PARAMETER ClusterIP
20+
IP Address of the failover cluster
21+
22+
.PARAMETER CloudWitnessAccount
23+
Name of the Azure storage account which should be used as cloud witness (empty if cloud witness should not be configure)
24+
25+
.PARAMETER CloudWitnessAccessKey
26+
Primary access Key of the Azure storage account which should be used as cloud witness (empty if cloud witness should not be configure)
27+
28+
.PARAMETER WitnessShare
29+
Full UNC path to the sahre which sould be used as witness file share (only if cloud witness can/sould not be used)
30+
31+
.PARAMETER HyperVHosts
32+
Name of the Hyper-V hosts on which the two VMs are currently running (to attach the need VHDX to the VMs)
33+
34+
.PARAMETER DataVHDXBasePaths
35+
Paths to the location where the VHDX files for the Data disks should be created (SOFS Share,CSV Volume, or local volume of the Hyper-V host)
36+
37+
.PARAMETER LogVHDXBasePaths
38+
Paths to the location where the VHDX files for the Log disks should be created (SOFS Share,CSV Volume, or local volume of the Hyper-V host)
39+
40+
.PARAMETER LogDiskSize
41+
Size fo the Log disk VHDX files in bytes (PowerShell will convert <number>GB automatically in the corresponding bytes value)
42+
43+
.PARAMETER DataDiskSize
44+
Size fo the Data disk VHDX files in bytes (PowerShell will convert <number>GB automatically in the corresponding bytes value)
45+
46+
.PARAMETER LogDiskLetter
47+
Drive Letter of the Log disk volume
48+
49+
.PARAMETER DataDiskLetter
50+
Drive Letter of the Data disk volume
51+
52+
.PARAMETER FSClusterName
53+
Name of the File Server Cluster Role
54+
55+
.PARAMETER FSClusterIP
56+
IP Address of the File Server Cluster Role
57+
58+
.PARAMETER ShareNames
59+
An Arry with Hashtable(s) with file share which sould be created on the Data volume. (One Hashtable per File Share)
60+
The Hashtable must contains to keys "Sharename" and "ContinuouslyAvailable"
61+
Example: @{ShareName = "TestShare";ContinuouslyAvailable=$true}
62+
63+
.PARAMETER ReplicationMode
64+
The replication mode of Storage Replica. Must be "Synchronous" or "Asynchronous"
65+
66+
.NOTES
67+
Version: 1.0.0.0, 03/07/2017 (stable)
68+
69+
Requires:
70+
PowerShell 5.0
71+
Hyper-V Cmdlets
72+
Failover Cluster Cmdlets
73+
Storage Cluster Cmdlets
74+
75+
.LINK
76+
@ Jonas Feller c/o J0F3, March 2017, www.jofe.ch
77+
78+
Get latest version at: https://github.com/J0F3/PowerShell/StorageReplica
79+
#>
80+
81+
Param
82+
(
83+
[String[]]
84+
$Servers = @('SR-SRV01','SR-SRV02'),
85+
86+
[String[]]
87+
$Locations = @('Bern', 'Zurich'),
88+
89+
[String]
90+
$ClusterName = 'SR-CLU01',
91+
92+
[String]
93+
$ClusterIP = '192.168.1.10',
94+
95+
[String]
96+
$CloudWitnessAccount = 'cloudwitness',
97+
98+
[String]
99+
$CloudWitnessAccessKey = 'fcNDPKdzxdTrbg3638ZvUDtrSfKTkAPLItQfsZ2suh10zLr8quWwDUXesIH8N6Wzyw==',
100+
101+
[String]
102+
$WitnessShare = '',
103+
104+
[String[]]
105+
$HyperVHosts = @('HV-SRV01','HV-SRV02'),
106+
107+
[String[]]
108+
$DataVHDXBasePaths = @('\\SOFS-Bern\csv01\SR-SRV01\Virtual Hard Disks\','\\SOFS-Zurich\csv01\SR-SRV02\Virtual Hard Disks\'),
109+
110+
[String[]]
111+
$LogVHDXBasePaths = @('\\SOFS-Bern\csv01\SR-SRV01\Virtual Hard Disks\','\\SOFS-Zurich\csv01\SR-SRV02\Virtual Hard Disks\'),
112+
113+
[long]
114+
$LogDiskSize = 10GB,
115+
116+
[long]
117+
$DataDiskSize = 127GB,
118+
119+
[string]
120+
$LogDiskLetter = 'L',
121+
122+
[string]
123+
$DataDiskLetter = 'D',
124+
125+
[string]
126+
$FSClusterName = 'SR-FS01',
127+
128+
[string]
129+
$FSClusterIP = '192.168.1.11',
130+
131+
[hashtable[]]
132+
$ShareNames = @(@{ShareName = "TestShare";ContinuouslyAvailable=$true}),
133+
134+
[string]
135+
[ValidateSet("Synchronous","Asynchronous")]
136+
$ReplicationMode = "Synchronous"
137+
)
138+
139+
# install features
140+
$Servers | ForEach-Object { Install-WindowsFeature -ComputerName $_ -Name Storage-Replica,Failover-Clustering,FS-FileServer -IncludeManagementTools -restart }
141+
142+
# build cluster
143+
New-Cluster -Name $ClusterName -Server$Servers[0] $Servers -StaticAddress $ClusterIP
144+
145+
# configure cluster quorum
146+
if($CloudWitnessAccount)
147+
{
148+
Set-ClusterQuorum -Cluster $ClusterName -CloudWitness -AccountName $CloudWitnessAccount -AccessKey $CloudWitnessAccessKey
149+
}
150+
elseif ($WitnessShare)
151+
{
152+
Set-ClusterQuorum -Cluster $ClusterName -FileShareWitness $WitnessShare
153+
}
154+
155+
# configure fault domains (sites) in cluster
156+
New-ClusterFaultDomain -CimSession $ClusterName -Name $Locations[0] -Type Site -Description "Primary" -Location $Locations[0]
157+
New-ClusterFaultDomain -CimSession $ClusterName -Name $Locations[1] -Type Site -Description "Secondary" -Location $Locations[1]
158+
Set-ClusterFaultDomain -CimSession $ClusterName -Name $Servers[0] -Parent $Locations[0]
159+
Set-ClusterFaultDomain -CimSession $ClusterName -Name $Servers[1] -Parent $Locations[1]
160+
(Get-Cluster -Name $ClusterName).PreferredSite=$Locations[0]
161+
162+
# Create new VHDX files
163+
# Log disks
164+
New-VHD -CimSession $HyperVHosts[0] -Path $($VHDXBasePaths[0] + 'Log.vhdx') -SizeBytes $LogDiskSize -Fixed
165+
New-VHD -CimSession $HyperVHosts[1] -Path $($VHDXBasePaths[1] + 'Log.vhdx') -SizeBytes $LogDiskSize -Fixed
166+
167+
# Data diks
168+
New-VHD -CimSession $HyperVHosts[0] -Path $($VHDXBasePaths[0] + 'Data.vhdx') -SizeBytes $DataDiskSize -Dynamic
169+
New-VHD -CimSession $HyperVHosts[1] -Path $($VHDXBasePaths[1] + 'Data.vhdx') -SizeBytes $DataDiskSize -Dynamic
170+
171+
172+
# Add new disks to cluster Server$Servers[0s
173+
Add-VMHardDiskDrive -CimSession $HyperVHosts[0] -VMName $Servers[0] -ControllerType SCSI -ControllerNumber 0 -ControllerLocation 1 -Path $($LogVHDXBasePaths[0] + 'Log.vhdx') -SupportPersistentReservationss
174+
175+
Add-VMHardDiskDrive -CimSession $HyperVHosts[0] -VMName $Servers[0] -ControllerType SCSI -ControllerNumber 0 -ControllerLocation 2 -Path $($DataVHDXBasePaths[0] + 'Data.vhdx') -SupportPersistentReservations
176+
177+
Add-VMHardDiskDrive -CimSession $HyperVHosts[1] -VMName $Servers[1] -ControllerType SCSI -ControllerNumber 0 -ControllerLocation 1 -Path $($LogVHDXBasePaths[1] + 'Log.vhdx') -SupportPersistentReservations
178+
179+
Add-VMHardDiskDrive -CimSession $HyperVHosts[1] -VMName $Servers[1] -ControllerType SCSI -ControllerNumber 0 -ControllerLocation 2 -Path $($DataVHDXBasePaths[1] + 'Data.vhdx') -SupportPersistentReservations
180+
181+
# Format disks on first node
182+
Invoke-Command -ComputerName $Servers[0] -ScriptBlock {
183+
Set-Disk -Number 1 -IsOffline $false
184+
set-disk -Number 2 -IsOffline $false
185+
Set-Disk -Number 1 -IsReadOnly $false
186+
set-disk -Number 2 -IsReadOnly $false
187+
188+
Initialize-Disk -Number 1 -PartitionStyle GPT
189+
Initialize-Disk -Number 2 -PartitionStyle GPT
190+
191+
Get-disk | Where-Object {$_.Size -eq 10gb -and $_.DiskNumber -ne $null} | New-Partition -UseMaximumSize -DriveLetter $USING:LogDiskLetter | Format-Volume -FileSystem NTFS -NewFileSystemLabel "Log"
192+
Get-disk | Where-Object {$_.Size -gt 10gb -and $_.DiskNumber -ne $null} | New-Partition -UseMaximumSize -DriveLetter $USING:DataDiskLetter | Format-Volume -FileSystem NTFS -NewFileSystemLabel "Data"
193+
}
194+
195+
# Add disks of first node to the cluster
196+
Get-ClusterAvailableDisk -All -Cluster $ClusterName | Add-ClusterDisk
197+
198+
# Rename cluster resources of source node
199+
Invoke-Command -ComputerName $Servers[0] -ScriptBlock {
200+
$ClusterDisks = Get-ClusterResource | Where-Object ResourceType -EQ 'Physical Disk'
201+
202+
foreach ($ClusterDisk in $ClusterDisks)
203+
{
204+
$DiskResource = Get-WmiObject MSCluster_Resource -Namespace root/mscluster | Where-Object{ $_.Name -eq $ClusterDisk.Name }
205+
$Disk = Get-WmiObject -Namespace root/mscluster -Query "Associators of {$DiskResource} Where ResultClass=MSCluster_Disk"
206+
$Partition = Get-WmiObject -Namespace root/mscluster -Query "Associators of {$Disk} Where ResultClass=MSCluster_DiskPartition"
207+
208+
$ClusterDisk.Name = "$($Partition.VolumeLabel)-Source"
209+
}
210+
}
211+
212+
# Create File Server Cluster Role
213+
Add-ClusterFileServerRole -Cluster $ClusterName -Name $FSClusterName -StaticAddress $FSClusterIP -Storage "Data-Source"
214+
215+
# Create File Shares
216+
foreach($Share in $ShareNames) {
217+
$SharePath = Join-Path -path "${DataDiskLetter}:" -ChildPath $($Share.ShareName)
218+
Invoke-Command -ComputerName $Servers[0] -ScriptBlock {mkdir $USING:SharePath}
219+
New-SmbShare -CimSession $Servers[0] -Name $Share.ShareName -Path $SharePath -ContinuouslyAvailable $Share.ContinuouslyAvailable
220+
}
221+
222+
# Configure Storage Replica to destination node/site
223+
224+
# Format disks on second node
225+
Invoke-Command -ComputerName $Servers[1] -ScriptBlock {
226+
Set-Disk -Number 1 -IsOffline $false
227+
set-disk -Number 2 -IsOffline $false
228+
Set-Disk -Number 1 -IsReadOnly $false
229+
set-disk -Number 2 -IsReadOnly $false
230+
231+
Initialize-Disk -Number 1 -PartitionStyle GPT
232+
Initialize-Disk -Number 2 -PartitionStyle GPT
233+
234+
Get-disk | Where-Object {$_.Size -eq 10gb -and $_.DiskNumber -ne $null} | New-Partition -UseMaximumSize -DriveLetter $USING:LogDiskLetter | Format-Volume -FileSystem NTFS -NewFileSystemLabel "Log"
235+
Get-disk | Where-Object {$_.Size -gt 10gb -and $_.DiskNumber -ne $null} | New-Partition -UseMaximumSize -DriveLetter $USING:DataDiskLetter | Format-Volume -FileSystem NTFS -NewFileSystemLabel "Data"
236+
}
237+
238+
# Add disks of second node to the cluster
239+
Get-ClusterAvailableDisk -All -Cluster $ClusterName | Add-ClusterDisk
240+
241+
# Move available storage to second node
242+
Move-ClusterGroup -Name "Available Storage" -Node $Servers[1]
243+
244+
# Rename cluster resources of destination node
245+
Invoke-Command -ComputerName $Servers[1] -ScriptBlock {
246+
$ClusterDisks = Get-ClusterResource | Where-Object {$_.ResourceType -EQ 'Physical Disk' -and $_.Name -notlike "*-Source"}
247+
248+
foreach ($ClusterDisk in $ClusterDisks)
249+
{
250+
$DiskResource = Get-WmiObject MSCluster_Resource -Namespace root/mscluster | Where-Object{ $_.Name -eq $ClusterDisk.Name }
251+
$Disk = Get-WmiObject -Namespace root/mscluster -Query "Associators of {$DiskResource} Where ResultClass=MSCluster_Disk"
252+
$Partition = Get-WmiObject -Namespace root/mscluster -Query "Associators of {$Disk} Where ResultClass=MSCluster_DiskPartition"
253+
254+
$ClusterDisk.Name = "$($Partition.VolumeLabel)-Destination"
255+
}
256+
}
257+
258+
# Configure Storage Replica
259+
New-SRPartnership -SourceComputerName $Servers[0] -SourceRGName "RG-Data-$($Servers[0])" -SourceRGDescription "Replication Group for D: from $($Servers[0]) to $($Servers[1])" -SourceVolumeName $DataDiskLetter -SourceLogVolumeName $LogDiskLetter -DestinationComputerName $Servers[1] -DestinationRGName "RG-Data-$($Servers[1])" -DestinationRGDescription "Replication Group for D: from $($Servers[0]) to $($Servers[1])" -DestinationVolumeName $DataDiskLetter -DestinationLogVolumeName $LogDiskLetter -ReplicationMode $ReplicationMode
260+
261+
#Check status of inital sync
262+
do{
263+
$r=(Get-SRGroup -ComputerName $Servers[1] -Name "RG-Data-$($Servers[1])").replicas
264+
[System.Console]::Write("Number of remaining bytes {0}`n", $r.NumOfBytesRemaining)
265+
Start-Sleep 10
266+
}until($r.ReplicationStatus -eq 'ContinuouslyReplicating')
267+
Write-Output "Replica Status: "$r.replicationstatus

0 commit comments

Comments
 (0)