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:
Operating systems supported:
The following cases are addressed by the sample scripts:
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.
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.
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.
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:
############################################################################ #
# USAGE: .\
#
# Where,
# and
#
# 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 $InputFile
if(!$?) {
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=$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
}
}
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:
SystemNames, Commands
SystemNames, Commands
CVMCLUSNODE1, vxdg list; vxassist version; vxdisk list
CVMCLUSNODE2
CVMCLUSNODE3
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:
############################################################################
#
# USAGE: .\
#
# Where,
# and
#
# 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."
}To create the input CSV file:
SystemNames, Commands
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
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:
############################################################################
#
# USAGE: .\
#
# Where,
# and
#
# 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."
}# 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"To create the input CSV file:
SystemNames,
Here,
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: 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.
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
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>.\
For example:
--> C:\Windows\System32>.\Case1_Script.ps1 -InputFile Case1_Inputs.csv
The following are the limitations for the scripts for SFW: