Problem
Introduction
This article provides information about sample PowerShell scripts that allow you to automate Storage Foundation for Windows (SFW) tasks. Using these scripts you can perform basic SFW operations on multiple systems simultaneously. The article covers details about these sample scripts and describes how to create and use them.
Products supported:
- Veritas Storage Foundation 6.0 for Windows and later
- Veritas Storage Foundation and High Availability 6.0 for Windows and later
Operating systems supported:
- Windows Server 2008 SP2, Windows Server 2008 R2
- Windows Server 2012, Windows Server 2012 R2
Solution
About the scripts
The following cases are addressed by the sample scripts:
- Case 1: Run one or more SFW commands on multiple systems
This sample script runs SFW commands on local and remote systems simultaneously. Use this script to query information such as SFW product version, disks, disk groups, and volumes configured, and active SFW tasks running on the systems.
- Case 2: Run one or more SFW commands on specific systems
This sample script runs specific SFW commands on specific local and remote systems simultaneously. Use this script to perform basic SFW operations such as creating disk groups and volumes, and adding a mirror for an existing volume.
- Case 3: Run a command with arguments on specific systems
This sample script runs SFW commands with arguments on specific local and remote systems simultaneously. Use this script to perform SFW operations that require multiple arguments, such as Volume Shadow Service (VSS) and volume level snapshotting operations that include preparing a snapshot, taking a snapshot, and running snapback.
Creating the script and the input CSV file
To automate SFW operations using these sample scripts, you first create a Windows PowerShell script file and a corresponding character-separated values (CSV) file. The script file contains the automated script and the CSV file contains the inputs such as the SFW commands, arguments, and the list of systems on which to run those commands.
The CSV file serves as the input file for the script. The script file reads the commands and the systems information from the CSV file. After the scripts and input parameter files are created, you execute these scripts using Windows PowerShell.
Note: The following instructions use Windows Notepad. Use any text editor to create the script and the CSV files.
Case 1: Run one or more SFW commands on multiple systems
This script runs SFW commands on multiple systems simultaneously.
To create a script file:
- Launch Windows Notepad and click File > New to create a blank document.
- Copy the following script as-is in to the blank document:
-->############################################################################ #
# USAGE: .\<Case1_Script.ps1> -InputFile <Case1_Inputs.CSV>
#
# Where, <Case1_Script.ps1> is the name of the PowerShell script file
# and <Case1_Inputs.CSV> is the name of the CSV inputs file.
#
# DESCRIPTION: This script runs SFW commands on all the remote SFW
# systems simultaneously using inputs from the CSV file.
#
############################################################################
param($InputFile)$Hostlist=@()
$Commands=@()
$Max_Threads=50
$Timeout_in_Seconds=600
if($InputFile -notmatch '.csv' -or $InputFile -notmatch '.CSV')
{
write-host "`n Error: Input file is incorrect. Provide a .csv file."
exit(0)
}
# Reads the .csv file for inputs.
$Csv = Import-Csv $InputFileif(!$?) {
write-host "`n`t Error: Input file not found."
exit(0) }
ForEach ($line in $Csv){# Reads the file and creates a list of system names.
if($line.SystemNames -ne $null)
{
$Hostlist += $line.SystemNames.Trim(" ")
}
if( $line.Commands -ne $null ){
$Commands += $line.Commands
}
}
$Command = $Commands -split ";"# Reads the file and extracts the commands.
$logfile="$(Get-Date -f yyyyMMdd-HHmmss)_$($InputFile.trimend(".CSV").trimend(".csv")).txt"
echo "`n Running the script..."
foreach($cmd in $Command){
$Script = [scriptblock]::Create($cmd)$job = Invoke-Command -jobname WinRM –ThrottleLimit $Max_Threads -AsJob -ComputerName $Hostlist -ScriptBlock $Script
Wait-Job $job -Timeout $Timeout_in_Seconds
#Logging the job status.
foreach($j in $job.ChildJobs){
$logfile1=$logfileif ($j.Error -ne "null" -or $j.State -eq "Failed")
{
$Status=1
$logfile1="Error"+$logfile;
}
"Computer Name :"+$j.Location | out-file $logfile1 -append
"Start Time :"+$j.PSBeginTime | out-file $logfile1 -append
"End Time :"+$j.PSEndTime | out-file $logfile1 -append
"State :"+$j.State | out-file $logfile1 -append
"Command :"+$j.Command | out-file $logfile1 -append
if ($logfile -ne $logfile1)
{
"Error :"+$j.Error | out-file $logfile1 -append
}
"Output :`n" | out-file $logfile1 -append
Receive-Job -Job $j | out-file $logfile1 -append
$star="****************"
echo "`n$star$star$star`n"| out-file $logfile1 -append
echo "`n"| out-file $logfile1 -append
}
}
Remove-Job $job
"Completed running the script."
echo "`n Output logs are stored at: $logfile"
if ($Status -eq 1)
{
echo "`n Some commands have failed."
echo "`n Error logs are stored at: Error$logfile"
}
else
{
echo "`n All commands completed successfully."
} Click File > Save As and save the document with a .ps1 extension, with a name of your choice.
For example, you can specify the script file name as Case1_Script.ps1.
To create the input CSV file:
- Launch Windows Notepad and click File > New to create a blank document.
- Provide input information in the blank document as per the following format:
-->SystemNames, Commands
<SystemName1> , <CommandName1>; <CommandName2>; <CommandName3>; <CommandName4>
<SystemName2>
<SystemName3>
Here,
<SystemName> is the name of the remote SFW system where you want to run the commands
<CommandName> is the name of the SFW command that you want to run
For example,
-->-->SystemNames, Commands
CVMCLUSNODE1, vxdg list; vxassist version; vxdisk list
CVMCLUSNODE2
CVMCLUSNODE3
Ensure that the system names and commands are separated by a comma and all the commands are separated by a semi-colon. - Click File > Save As and save the document with a .csv extension, with a name of your choice.
For example, you can specify the input csv file name as Case1_Inputs.csv.
Case 2: Run one or more SFW commands on specific systems
This script runs specified SFW commands on specific systems simultaneously. All the commands listed against a system name in the CSV file are run on that particular system.
To create a script file:
- Launch Windows Notepad and click File > New to create a blank document.
- Copy the following script as-is in to the blank document:
-->############################################################################
#
# USAGE: .\<Case2_Script.ps1> -InputFile <Case2_Inputs.CSV>
#
# Where, <Case2_Script.ps1> is the name of the PowerShell script file
# and <Case2_Inputs.CSV> is the name of the CSV inputs file.
#
# DESCRIPTION: This script runs specified SFW commands on the
# corresponding remote SFW systems simultaneously using inputs from the CSV # file.
#
############################################################################
Param(
$InputFile,
$Max_Threads = 50,
$Timeout_in_Seconds=600
)
if($InputFile -notmatch '.csv' -or $InputFile -notmatch '.CSV')
{
write-host "`n Error: Input file is incorrect. Provide a .csv file."
exit(0)
}
$Csv = Import-Csv $InputFile
if(!$?)
{
write-host "`n`t Error: Input file not found."
exit(0)
}
Get-Job | Remove-Job -Force
$i = 0
$logfile="$(Get-Date -f yyyyMMdd-HHmmss)_$($InputFile.trimend(".CSV").trimend(".csv")).txt"
"Running the script..."
ForEach ($line in $Csv){
$ComputerName = $line.SystemNames
if( $ComputerName -eq $null -or $ComputerName -eq "" )
{
Write-Host "`n Error: Failed to Read the SystemNames from .CSV file ";
exit(0)
}
While ($(Get-Job -state running).count -ge $Max_Threads){
Write-Progress -Activity "Creating server list" -Status "Waiting for threads to close" -CurrentOperation "$i threads created - $($(Get-Job -state running).count) threads open" -PercentComplete ($i / $Csv.count * 100)
Start-Sleep -Milliseconds 500
}
if( $line.Commands -eq "" )
{
Write-Host "`t Warning: No command is specified for system $ComputerName ";
Write-Host "`t Continuing for other systems ";
continue
}
$i++
$script = [scriptblock]::Create($line.Commands)
$job =Invoke-Command -ScriptBlock $script -jobname WinRM -AsJob -ComputerName $ComputerName
Write-Progress -Activity "Creating server list" -Status "Starting threads" -CurrentOperation "$i threads created - $($(Get-Job -state running).count) threads open" -PercentComplete ($i / $Csv.count * 100)
}
$Complete = Get-date
While ($(Get-Job -State Running).count -gt 0){
$ComputersStillRunning = ""
ForEach ($System in $(Get-Job -state running)){$ComputersStillRunning += ", $($System.Location)"}
$ComputersStillRunning = $ComputersStillRunning.Substring(2)
Write-Progress -Activity "Receiving Jobs" -Status "$($(Get-Job -State Running).count) threads remaining" -CurrentOperation "$ComputersStillRunning" -PercentComplete ($(Get-Job -State Completed).count / $(Get-Job).count * 100)
If ($(New-TimeSpan $Complete $(Get-Date)).totalseconds -ge $Timeout_in_Seconds){"Killing all jobs still running . . .";Get-Job -State Running | Remove-Job -Force}
Start-Sleep -Milliseconds 500
}
#Logging the job status.
foreach($job in Get-Job){
foreach($j in $job.ChildJobs){
$logfile1=$logfile
if ($j.Error -ne "null" -or $j.State -eq "Failed")
{
$Status=1
$logfile1="Error"+$logfile;
}
"Computer Name :"+$j.Location | out-file $logfile1 -append
"Start Time :"+$j.PSBeginTime | out-file $logfile1 -append
"End Time :"+$j.PSEndTime | out-file $logfile1 -append
"State :"+$j.State | out-file $logfile1 -append
"Command :"+$j.Command | out-file $logfile1 -append
if ($logfile -ne $logfile1)
{
"Error :"+$j.Error | out-file $logfile1 -append
}
"Output :`n" | out-file $logfile1 -append
Receive-Job -Job $j | out-file $logfile1 -append
$star="****************"
echo "`n$star$star$star`n"| out-file $logfile1 -append
echo "`n"| out-file $logfile1 -append
}
}
"Completed running the script."
echo "`n Output logs are stored at: $logfile"
if ($Status -eq 1)
{
echo "`n Some commands have failed."
echo "`n Error logs are stored at: Error$logfile"
}
else
{
echo "`n All commands completed successfully."
}
- Click File > Save As and save the document with a .ps1 extension, with a name of your choice.
For example, you can specify the script file name as Case2_Script.ps1.
To create the input CSV file:
- Launch Windows Notepad and click File > New to create a blank document.
- Provide input information in the blank document as per the following format:
-->SystemNames, Commands
<SystemName1> , <CommandName1>; <CommandName2>; <CommandName3>; <CommandName4>
<SystemName2> , <CommandName1>; <CommandName2>
<SystemName3> , <CommandName1>; <CommandName2>; <CommandName3>
Here,
<SystemName> is the name of the SFW system where you want to run the commands
<CommandName> is the name of the SFW command that you want to run
For example:
-->SystemNames, Commands
W2K8R2CVM01, vxdg -g dg1 init harddisk1; vxassist -g dg1 make Vol1 50G
W2K8R2CVM02, vxassist -g dg2 make Vol2 500M driveletter=F:
W2K8R2CVM03, vxdg -g dg3 init harddisk5 harddisk6
W2K8R2CVM04, vxdg -g dg4 destroy –f
Ensure that the system names and commands are separated by a comma and all the commands are separated by a semi-colon.
- Click File > Save As and save the document with a .csv extension, with a name of your choice.
For example, you can specify the input csv file name as Case2_Inputs.csv.
Case 3: Run a command with arguments on specific systems
This script runs single argument-specific SFW command on specific systems simultaneously. This is useful for running a command with multiple arguments that you want to run on multiple systems. For this script, you need to modify the command and parameters in the script file in addition to providing inputs in the CSV file.
To create a script file:
- Launch Windows Notepad and click File > New to create a blank document.
- Copy the following script as-is in to the blank document:
-->############################################################################
#
# USAGE: .\<Case3_Script.ps1> -InputFile <Case3_Inputs.CSV>
#
# Where, <Case3_Script.ps1> is the name of the PowerShell script file
# and <Case3_Inputs.CSV> is the name of the CSV inputs file.
#
# DESCRIPTION: This script runs single argument-specific command on the
# corresponding remote SFW systems simultaneously using inputs from the CSV # file.
#
############################################################################
Param(
$InputFile,
$Max_Threads = 50,
$Timeout_in_Seconds=600
)
if($InputFile -notmatch '.csv' -or $InputFile -notmatch '.CSV')
{
write-host "`n Error: Input file is incorrect. Provide a .csv file."
exit(0)
}
$Csv = Import-Csv $InputFile
if(!$?)
{
write-host "`n`t Error: Input file not found."
exit(0)
}
Get-Job | Remove-Job -Force
$i = 0
$logfile="$(Get-Date -f yyyyMMdd-HHmmss)_$($InputFile.trimend(".CSV").trimend(".csv")).txt"
"Running the script..."
ForEach ($line in $Csv){
$ComputerName = $line.SystemNames
if( $ComputerName -eq $null -or $ComputerName -eq "" )
{
Write-Host "`n Error: Failed to Read the SystemNames from .CSV file ";
exit(0)
}
# Set the parameters for the command here:
$Dg = $line.DGNames
$Vol = $line.VolumeNames
$SnapPlex = $line.SnapPlexNames
$SnapVol = $line.SnapshotVolumeNames
# Provide the complete command here:
$command = "vxassist -g $Dg snapshot $Vol plex=$SnapPlex $SnapVol"
While ($(Get-Job -state running).count -ge $Max_Threads){
Write-Progress -Activity "Creating server list" -Status "Waiting for threads to close" -CurrentOperation "$i threads created - $($(Get-Job -state running).count) threads open" -PercentComplete ($i / $Csv.count * 100)
Start-Sleep -Milliseconds 500
}
$i++
$script = [scriptblock]::Create($command)
$job =Invoke-Command -ScriptBlock $script -jobname WinRM -AsJob -ComputerName $ComputerName
Write-Progress -Activity "Creating server list" -Status "Starting threads" -CurrentOperation "$i threads created - $($(Get-Job -state running).count) threads open" -PercentComplete ($i / $Csv.count * 100)
}
$Complete = Get-date
While ($(Get-Job -State Running).count -gt 0){$ComputersStillRunning = ""
ForEach ($System in $(Get-Job -state running)){$ComputersStillRunning += ", $($System.Location)"}
$ComputersStillRunning = $ComputersStillRunning.Substring(2)
Write-Progress -Activity "Receiving Jobs" -Status "$($(Get-Job -State Running).count) threads remaining" -CurrentOperation "$ComputersStillRunning" -PercentComplete ($(Get-Job -State Completed).count / $(Get-Job).count * 100)
If ($(New-TimeSpan $Complete $(Get-Date)).totalseconds -ge $Timeout_in_Seconds){"Killing all jobs still running . . .";Get-Job -State Running | Remove-Job -Force}
Start-Sleep -Milliseconds 500
}
"Reading all commands..."
foreach($job in Get-Job){
foreach($j in $job.ChildJobs){
$logfile1=$logfile
if ($j.Error -ne "null" -or $j.State -eq "Failed")
{
$Status=1
$logfile1="Error"+$logfile;
}
"Computer Name :"+$j.Location | out-file $logfile1 -append
"Start Time :"+$j.PSBeginTime | out-file $logfile1 -append
"End Time :"+$j.PSEndTime | out-file $logfile1 -append
"State :"+$j.State | out-file $logfile1 -append
"Command :"+$j.Command | out-file $logfile1 -append
if ($logfile -ne $logfile1)
{
"Error :"+$j.Error | out-file $logfile1 -append
}
"Output :`n" | out-file $logfile1 -append
Receive-Job -Job $j | out-file $logfile1 -append
$star="****************"
echo "`n$star$star$star`n"| out-file $logfile1 -append
echo "`n"| out-file $logfile1 -append
}
}
"Completed running the script."
echo "`n Output logs are stored at: $logfile"
if ($Status -eq 1)
{
echo "`n Some commands have failed."
echo "`n Error logs are stored at: Error$logfile"
}
else
{
echo "`n All commands completed successfully."
}
- In the script specified in the previous step, you need to provide the full command with arguments and also specify the parameters for which the script reads information from the CSV file.
The script specified in step 2 has the following two example parameters and a command:
--># Set the parameters for the command here:
$Dg = $line.DGNames
$Vol = $line.VolNames
$SnapPlex = $line.SnapPlexNames
$SnapVol = $line.SnapshotVolumeNames
# Provide the complete command here:
$command = "vxassist -g $Dg snapshot $Vol plex=$SnapPlex $SnapVol"
Where, in the example parameters, “DG Name” and “Volume Name” are the names of the columns in CSV that lists the disk group and volume names.
Note: Before using the script, replace the example parameters and command with your own.
- After making parameter and command related changes in the script as per your requirement, click File > Save As and save the document with a .ps1 extension, with a name of your choice.
For example, you can specify the script file name as Case3_Script.ps1.
To create the input CSV file:
- Launch Windows Notepad and click File > New to create a blank document.
- Provide input information in the blank document as per the following format:
SystemNames, <ParameterName1>, <ParameterName2>, <ParameterName3>
<SystemName1> , <ParameterValue1>, <ParameterValue1>, <ParameterValue1>
<SystemName2> , <ParameterValue2>, <ParameterValue2>, <ParameterValue2>
<SystemName3> , <ParameterValue3>, <ParameterValue3>, <ParameterValue3>
<SystemName> is the name of the SFW system where you want to run the commands
<ParameterName> is the name of the parameter column
<ParameterValue> is the value of the parameter for the SFW command
For example:
-->SystemNames, DGNames, VolumeNames, SnapPlexNames, SnapshotVolumeNames
CVMCLUSNODE1, dg1, Vol1, Vol1-02, SnapVol1
CVMCLUSNODE2, dg2, Vol2, Vol2-02, SnapVol2
CVMCLUSNODE3, dg3, Vol3, Vol3-02, SnapVol3
W2K8R2CVM01, dg1, Vol1, Vol1-02, SnapVol1
W2K8R2CVM02, dg2, Vol2, Vol2-02, SnapVol2
Note: The name that you provide for the <ParameterName> columns also needs to be used in the script where you specify the parameters.
Ensure that the system names and parameters are separated by a comma.
- Click File > Save As and save the document with a .csv extension, with a name of your choice.
For example, you can specify the inputs csv file name as Case3_Inputs.csv.
Note: You can change the Max Threads and Timeout values as per your requirement. Max Threads are the number of systems on which the commands are run simultaneously and Timeout (in seconds) is the maximum time in which the commands will be run. Value for the Timeout_in_Seconds must be greater than the time required by the most time-consuming command. The default values are: $Max_Threads = 50 and $Timeout_in_Seconds=600.
Prerequisites for running the scripts
Before you run the scripts using Windows PowerShell, ensure that the following prerequisites are met:
On the remote systems where you want to run the SFW commands, enable remote computing by running the following command in PowerShell:
Enable-PSRemoting –force- On the local system, add the remote system to the trusted list in PowerShell by running the following command in PowerShell:
Note: If the systems are part of the same domain, then you do not need to run this command.
--> Set-Item wsman:\localhost\Client\TrustedHosts -value <RemoteSystemIPAddress>
- On the local system, set the PowerShell execution policy to RemoteSigned by running the following command in PowerShell:
-->Set-ExecutionPolicy RemoteSigned
- The local and remote systems are running under the same user credentials.
- All the remote systems should be reachable by their host names from the local system.
Running the scripts
After the scripts for SFW are created, using Windows PowerShell, you can run any of the three scripts for performing various SFW operations as follows:
On the local system, run any of the script by running the following command in PowerShell:
--> C:\Windows\System32>.\<PowerShellScriptFileName> -InputFile <CSVInputFileName>
For example:
--> C:\Windows\System32>.\Case1_Script.ps1 -InputFile Case1_Inputs.csv
Limitations
The following are the limitations for the scripts for SFW:
- You must specify correct and complete commands. Partially correct commands, for example vxdg list11 or vxasssist makeee, are incorrectly logged as “Successful” in the log files even though such commands result in failure.
- If the local system is running on Windows Server 2008 SP2 or 2008 R2, then the start and end times are not displayed in the log files. This is a limitation of PowerShell on those operating systems.