#Import PVS PowerShell Module
#This script file is intended to provide an example script of how vDisk replication can be performed.

#This script was created by Rob Zylowski Citrix CCS with help from many folks
#It should be used as an example of how to perform PVS Disk replication.
#This script will interrogate defined PVS master Servers to compile a list of stores and store paths.
#
#Although every effort has been made to ensure that these sample scripts work properly, 
#Citrix disclaims any responsibility for any and all liability or responsibility for any 
#damages that may result from using these scripts. 
#The sample scripts are provided to you as is, with no warranty or guarantees.
#
#Version 2.1 7-10-2018
#Version 2.4 11-12-2018
#Version 3.2.3 3-2-2019

try{
    Import-Module 'C:\Program Files\Citrix\Provisioning Services console\Citrix.PVS.SnapIn.dll' -ErrorAction Stop
} catch {
    Write-Host "PVS PowerShell Snap-In Not Found" -BackgroundColor Black -ForegroundColor Red
    $msg = "PVS PowerShell Snap-In Not Found on $env:computername.  Script terminated."
    Write-EventLog -LogName Application -Source $logSource -EventId 2 -EntryType Error -Message $msg -Category 0
    exit
}


function Get-ScriptDirectory
#This gets the current directlry which we use to store the text data files
{
 $Invocation = (Get-Variable MyInvocation -Scope 1).Value
 Split-Path $Invocation.MyCommand.Path
}

$ScriptSource = Get-ScriptDirectory

Write-Host "Preparing DataTable object"
# Prepare global PathTable object
$Global:PVSTable = New-Object system.Data.DataTable "PVSTable"
[array]$Columns = $("MasterFarm","MasterSite", "MasterServer", "MasterStore","MasterPath", "SlaveFarm", "SlaveSite", "SlaveServer", "SlaveStore", "SlavePath", "Status", "IsSelected")
ForEach ($Column in $Columns) {$PVSTable.Columns.Add($(New-Object system.Data.DataColumn $Column,([string])))}
ForEach ($Column in $PVSTable.Columns) {$Column.DataType = [Object]}


$MastersFile = Get-Content "$ScriptSource\..\config\masters.txt" -Encoding ASCII
$Masters = New-Object System.Collections.ArrayList

if ($MastersFile -is [System.Array])
{
	$FirstMaster = $MastersFile[0].ToLower()
	foreach ($MasterServer in $MastersFile)
	{
		if ($MasterServer -ne ""){$Masters.Add($MasterServer)>$null}
	}
}
else
{
	$FirstMaster = $MastersFile.ToLower()
	$Masters.Add($MastersFile)>$null
}

$SMFilePath = "$ScriptSource\..\config\SecondaryMasters.txt"
if (Test-Path $SMFilePath)
{
	$SecondaryMasters = Get-Content -LiteralPath $SMFilePath  -Encoding Ascii
}

foreach ($line in $SecondaryMasters)
{
	$SecMasterServerInfo = $line -split ";"
	$Masters += $SecMasterServerInfo[2]
}

#Need an Array for the source/dest pairs already added
$arSourceDest = New-Object System.Collections.ArrayList
$PrimaryMasterStores = @{}

#get Stores for Master which we will use to add Farm to Farm Stores
Set-PvsConnection -Server $FirstMaster -ErrorAction SilentlyContinue
$MasterStoresArray = Get-PvsStore

foreach($Store in $MasterStoresArray)
{
	$ServerStorePathOrig =  $Store.Path
	If ($ServerStorePathOrig[1] -eq ":") 
	{
		$ServerStorePath = $ServerStorePathOrig.Replace(":", "$")
		$StorePath = "\\" +$FirstMaster + "\" + $ServerStorePath
	}
	else
	{
		$StorePath = $ServerStorePathOrig
	}
	$StoreNamePrimary = $Store.Name
	if ($StoreSiteName -eq "")
	{
		Write-Host -ForegroundColor Blue "Store [$StoreNamePrimary] has no site defined on the Primary Master Server [$FirstMaster]"
		$StoreSiteName = "Shared"
	}
	$PrimaryMasterStores.Add($StoreNamePrimary,$StorePath)>$null
}

foreach ($MasterServerName in $Masters)
{
	Write-Host -ForegroundColor Blue "======= Connecting to Master Server $MasterServerName =========="
	Set-PvsConnection -Server $MasterServerName -ErrorAction SilentlyContinue
	if (!$?)
	{
		Write-Host -ForegroundColor Red "Could not connect to Server [$MasterServerName] will skip processing for this server"
		continue
	}
	
	Write-Host "Getting Farm info for this server"
	$FarmInfo = Get-PvsFarm 
	$MasterFarm = $FarmInfo.FarmName
	Write-Host "Getting Server Info"
	$MasterServer = Get-PvsServer -ServerName $MasterServerName
	$MasterSite = $MasterServer.SiteName
	$MasterStores = @{}
	
	#get Stores for Master
	$MasterStoresArray = Get-PvsStore
	foreach($Store in $MasterStoresArray)
	{
		$ServerStorePathOrig =  $Store.Path
		$ServerStorePath = $ServerStorePathOrig.Replace(":", "$")
		$StorePath = $ServerStorePath
		$MasterStores.Add($Store.StoreName,$Store.Path)>$null
	}

	if ($MasterServerName -eq $firstMaster)
	{
		$FirstMasterFQDN=$MasterServer.ServerFqdn
		$FirstMasterFarm = $FarmInfo.FarmName
		$FirstMasterSite = $MasterServer.SiteName
	}
	
	# Creating global list
	Write-Host "Retrieving all required objects from PVS"
	[array]$AllSites = Get-PvsSite
	[array]$AllServers = Get-PvsServer
	[array]$AllStores = Get-PvsStore
	
	# Generate DataTable
	Write-Host
	ForEach ($Site in $AllSites) 
	{
		[string]$SiteID = $Site.SiteID
		$SlaveSite = $Site.SiteName
		
		Write-Host -NoNewline "Retrieving replica servers for site "; Write-Host -ForegroundColor Blue $($Site.SiteName)
		ForEach ($Server in $AllServers) 
		{
			$ServerName = $Server.Name
			$ServerSiteName = $Server.SiteName
			if ($Site.SiteName -ne $ServerSiteName){continue}
			Write-Host "Retrieving stores associated with server " -NoNewline
			Write-Host -ForegroundColor Blue $ServerName
			$ServerStores = $null
			
			$ServerStores = Get-PVSServerStore -serverid $Server.serverId
			if (!$?)
			{
				write-host -ForegroundColor Red "Error retrieving $ServerName info "
				write-host -ForegroundColor black 
				continue
			}
			
			ForEach ($ServerStore in $ServerStores) 			
			{
				
				$MasterStoreName = $ServerStore.StoreName
				Write-Host "Retrieving slave servers associated with store " -NoNewline; Write-Host -ForegroundColor Blue "$MasterStoreName"
				$SlaveServers = Get-PVSServer -ServerName $ServerName -ErrorAction SilentlyContinue
				if (!$?)
				{
					write-host -ForegroundColor Red "Error retrieving Slave Server info for [$ServerName]" 
					continue
				}
				
				ForEach ($Slave in $SlaveServers) 
				{
					$bKeepRecord = $true
					$SlaveServerName = $Slave.ServerName
					$SlaveFarmInfo = $null
					
					#Connect to PVS Server to get FARM
					Set-PvsConnection -Server $SlaveServerName -ErrorAction SilentlyContinue 
					if (!$?)
					{
						write-host -ForegroundColor Red "Error connecting to Slave Server [$ServerName]"
						$SlaveFarm = "NA"
					}
					else
					{
						$SlaveFarmInfo = Get-PvsFarm
						$SlaveFarm = $SlaveFarmInfo.FarmName
					}
					

					$SlaveSite = $Slave.SiteName
					
					#Here we need to capture the primary master as the source
					if ($MasterServerName -eq $SlaveServerName)
					{
						$MasterStorePathInfo = $PrimaryMasterStores.get_Item($MasterStoreName)
						$MasterStorePath = $MasterStorePathInfo[1]
					}
					else
					{
						$MasterStorePath = $MasterStores.get_Item($MasterStoreName)	
					}
			
					$SlaveStores = Get-PvsStore -ServerName $Slave.ServerName
					$SlaveStorePath = ""
					foreach ($SlaveStore in $SlaveStores)
					{
						if ($SlaveStore.Name -eq $MasterStoreName)
						{
							$SlaveStorePath = $SlaveStore.Path
						}
					}
					#Source
					$PathIsShare = $true
					If ($MasterStorePath[1] -eq ":") {
						$MasterStorePath = "\\$masterServerName\" +  $MasterStorePath.Replace(":", "$")
						$PathIsShare = $false
					}
					#Destination
					$DPathIsShare = $true
					If ($SlaveStorePath[1] -eq ":") {
						$SlaveStorePath = $SlaveStorePath.Replace(":", "$")
						$DPathIsShare = $false
					}
					if(!$DPathIsShare)
					{					
						$SlaveStorePath="\\$SlaveServerName\$SlaveStorePath"
					}
					
					$TableRow = $PVSTable.NewRow() 
					$TableRow.Status = "Initialized"
					$TableRow.MasterFarm = $MasterFarm
					$TableRow.MasterSite = $MasterSite
					$TableRow.MasterServer = $MasterServer.ServerName
					$TableRow.MasterStore = $MasterStoreName
					If ($MasterStorePath[1] -eq ":") 
					{
						$OurStorePathRight = $MasterStorePath.Replace(":", "$")
						$OurMasterStorePath = "\\" + $MasterServer.ServerName + "\$OurStorePathRight"
					}
					else
					{
						$OurMasterStorePath = $MasterStorePath
					}
					$TableRow.MasterPath = $OurMasterStorePath
					$TableRow.SlaveFarm = $SlaveFarm
					$TableRow.SlaveSite = $SlaveSite
					$TableRow.SlaveServer = $SlaveServerName
					$TableRow.SlaveStore = $ServerStore.StoreName
					If ($SlaveStorePath[1] -eq ":") 
					{
						$OurStorePathRight = $SlaveStorePath.Replace(":", "$")
						$OurSlaveStorePath = "\\" + $MasterServer.Name + "\$OurStorePathRight"
					}
					else
					{
						$OurSlaveStorePath = $SlaveStorePath
					}
					$TableRow.SlavePath = $OurSlaveStorePath
					$TableRow.IsSelected = $False
					
					#===============================================================
					# Skip Record Rules
					#===============================================================
					
					if ($MasterServerName -ne $SlaveServerName)
					{
					
						if ($MasterStorePath -eq $SlaveStorePath)
						{
							$myStoreNAme = $ServerStore.StoreName
							Write-Host "Master Store Path and Slave Store Path are the same for Store [$myStoreNAme]"
							continue
						}
					}
					
					if ($MasterServerName -ne $FirstMaster)
					{
						If ($OurSlaveStorePath -like "*$FirstMaster*")
						{
							$bKeepRecord = $false
						}
						If ($MasterSite -ne $SlaveSite)
						{
							$bKeepRecord = $false
						}
					}
					
					if ($MasterServerName -eq $SlaveServerName)
					{
						$bKeepRecord = $false
					}		
					
					if ($arSourceDest -contains "$OurMasterStorePath$OurSlaveStorePath")
					{
						$bKeepRecord = $false
					}
					if ($OurMasterStorePath -eq $OurSlaveStorePath)
					{
						$bKeepRecord = $false
					}
					If ($MasterStoreName -ne $ServerStore.StoreName)
					{
						$bKeepRecord = $false
					}
					
					#===============================================================
					
					#If we make it this far keep the record in PVSTable
					
					if ($bKeepRecord)
					{
						$PVSTable.Rows.Add($TableRow)
						$arSourceDest.add("$OurMasterStorePath$OurSlaveStorePath")>$null
					}
					
					#Now if this is a secondary master we want to add the same slave store to be able to copy from the Primary Master
					
					if ($MasterServerName -ne $FirstMaster)
					{
					 	foreach ($MasterStoreKey in $PrimaryMasterStores.keys)
						{
						
							$PrimaryMasterStoreName = $MasterStoreKey
							$PrimaryMasterStorePath = $PrimaryMasterStores.get_Item($MasterStoreKey)
							
							$TableRow = $PVSTable.NewRow() 
							$TableRow.Status = "Initialized"
							$TableRow.MasterFarm = $FirstMasterFarm
							$TableRow.MasterSite = $FirstMasterSite
							$TableRow.MasterServer = $FirstMaster
							$TableRow.MasterStore = $PrimaryMasterStoreName

							If ($PrimaryMasterStorePath[1] -eq ":") 
							{
								$OurStorePathRight = $PrimaryMasterStorePath.Replace(":", "$")
								$OurMasterStorePath = "\\" + $FirstMaster + "\$OurStorePathRight"
							}
							else
							{
								$OurMasterStorePath = $PrimaryMasterStorePath
							}
							$TableRow.MasterPath = $OurMasterStorePath
							$TableRow.SlaveFarm = "$SlaveFarm"
							$TableRow.SlaveSite = $SlaveSite
							$TableRow.SlaveServer = $SlaveServerName
							$TableRow.SlaveStore = $ServerStore.StoreName

							If ($SlaveStorePath[1] -eq ":") 
							{
								$OurStorePathRight = $SlaveStorePath.Replace(":", "$")
								$OurSlaveStorePath = "\\" +$SlaveServerName + "\$OurStorePathRight"
							}
							else
							{
								$OurSlaveStorePath = $SlaveStorePath
							}
							$TableRow.SlavePath = $OurSlaveStorePath
							$TableRow.IsSelected = $False
							
							if ($arSourceDest -notcontains "$OurMasterStorePath$OurSlaveStorePath")
							{
								if ($OurMasterStorePath -ne $OurSlaveStorePath)
								{
									If ($PrimaryMasterStoreName -eq $ServerStore.StoreName)
									{
										$PVSTable.Rows.Add($TableRow)
										$arSourceDest.add("$OurMasterStorePath$OurSlaveStorePath")>$null
									}
								}
							}
						}
					}
				}
			}
		}
	}
}

Write-Host
Write-Host -ForegroundColor Blue "Validating source and destination folders"
# Identify source and target directories
ForEach ($TableRow in $PVSTable.Rows) {
	
	$TableRow.Status = "Path specified"
	
	Write-Host "Source:" $TableRow.MasterPath -NoNewline
	If ($(Test-Path $TableRow.MasterPath) -eq $true) {
		$TableRow.Status = "Source validated"
		Write-Host -ForegroundColor Green " OK " -NoNewline
	} Else {
		$TableRow.Status = "Source not available"
		Write-Host -ForegroundColor Red " KO " -NoNewline
		#Continue
	}
	
	Write-Host -ForegroundColor Blue  " Destination:" $TableRow.SlavePath -NoNewline
	If ($(Test-Path $TableRow.SlavePath) -eq $true) {
		$TableRow.Status = "Destination validated"
		Write-Host -ForegroundColor Green " OK"
		$TableRow.IsSelected = $True
	} Else {
		$TableRow.Status = "Destination not available"
		Write-Host -ForegroundColor Red " KO"
		$TableRow.IsSelected = $True 
	}
}

if ($PVSTable.Rows.Count -gt 0)
{
	#Now write the table out to a config file
	$storerowsfile = "$ScriptSource\..\config\storerows.txt"
	if(Test-Path $storerowsfile){del $storerowsfile}
	$i=1
	$j=0
	$FirstTime=$true
	ForEach ($row in $PVSTable.Rows) 
	{

		$MasterFarm = $row.MasterFarm
		$MasterSite = $row.MasterSite
		$MasterServer = $row.MasterServer
		$MasterStore = $row.MasterStore
		$MasterPath = $row.MasterPath
		$SlaveFarm = $row.SlaveFarm
		$SlaveSite = $row.SlaveSite
		$SlaveServer = $row.SlaveServer
		$SlaveStore = $row.SlaveStore
		$SlavePath = $row.SlavePath
		$rowInclude="False"

		$rowoutput="$MasterFarm;$MasterSite;$MasterServer;$MasterStore;$MasterPath;$SlaveFarm;$SlaveSite;$SlaveServer;$SlaveStore;$SlavePath;$rowInclude;$j"
		$rowoutput | Out-File -LiteralPath $storerowsfile -Append -Encoding ASCII
		$i++
		$firstTime = $false
		$PrevRowMasterName=$rowMasterName
	}
}





