Automation of Storage Foundation for Windows (SFW) operations using PowerShell scripts

Article: 100014072
Last Published: 2014-09-19
Ratings: 0 0
Product(s): InfoScale & Storage Foundation

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:

  1. Launch Windows Notepad and click File > New to create a blank document.
  2. 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 $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."

           }
  3. 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:

  1. Launch Windows Notepad and click File > New to create a blank document.
  2. 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.
  3. 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:

  1. Launch Windows Notepad and click File > New to create a blank document.
  2. 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."

      }
     
  3. 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:

  1. Launch Windows Notepad and click File > New to create a blank document.
  2. 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.
     
  3. 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:

  1. Launch Windows Notepad and click File > New to create a blank document.
  2. 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."

         }
     
  3. 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.
     
  4. 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:

  1. Launch Windows Notepad and click File > New to create a blank document.
  2. 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>

     

    Here,
    <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.
     
  3. 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.

 


Was this content helpful?