How to execute PowerShell script (ps1 file) from C# code


Tarun Kumar Chatterjee
.Net – Technology Specialist
Published On :   31 May 2016
Visit Count
Today :  23    Total :   6722



There are two way-out I found out to call the ps1 file from C#:

1. Created a batch file which will have the code to execute the ps1 file & the batch file will be executed by C# code

Batch files Code:

@echo offPowershell.exe -executionpolicy remotesigned -File  <ps1 file path>

C# code to execute the batch file:

 System.Security.SecureString pass = new System.Security.SecureString();
             string strPass = Admin Password";
             foreach (char c in strPass)
             {
                 pass.AppendChar(c);
             }
 System.Diagnostics.Process.Start(@"Batch File Path","Admin User ID", pass,"Domain Name");
 

2. Created RunSpace & Pipeline to execute the ps1 file directly from C# code

 private Collection<PSObject> RunPsScript(string psScriptPath)
         {
             string psScript = string.Empty;
             if (File.Exists(psScriptPath))
                 psScript = File.ReadAllText(psScriptPath);
             else
                 throw new FileNotFoundException("Wrong path for the script file");
             RunspaceConfiguration config = RunspaceConfiguration.Create();
             PSSnapInException psEx;
             //add Microsoft SharePoint PowerShell SnapIn
             PSSnapInInfo pssnap = config.AddPSSnapIn("Microsoft.SharePoint.PowerShell", out  psEx);
             //create powershell runspace
             Runspace cmdlet = RunspaceFactory.CreateRunspace(config);
             cmdlet.Open();
             RunspaceInvoke scriptInvoker = new RunspaceInvoke(cmdlet);
             // set powershell execution policy to unrestricted
             scriptInvoker.Invoke("Set-ExecutionPolicy Unrestricted");
             // create a pipeline and load it with command object
             Pipeline pipeline = cmdlet.CreatePipeline();
             try
             { 
                 // Using Get-SPFarm powershell command 
                 pipeline.Commands.AddScript(psScript);
                 pipeline.Commands.AddScript("Out-String");
                 // this will format the output
                 Collection<PSObject> output = pipeline.Invoke();
                 pipeline.Stop();
                 cmdlet.Close();
                 // process each object in the output and append to stringbuilder  
                 StringBuilder results = new StringBuilder();
                 foreach (PSObject obj in output)
                 {
                     results.AppendLine(obj.ToString());
                 }
                 return output;
             }
             catch(Exception ex)
             {                
                 return null;
             }
 }
 

Here is my ps1 file code to delete the list items:

 if ((Get-PSSnapin -Name "Microsoft.SharePoint.PowerShell" ) -eq $null) {
     Add-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue  
 }
 
 $rootWeb= Get-Spweb <"Site URL">   
 $listToProcess = $rootWeb.GetList("List URL") 
 $NumberOfItemsToDeleteInBatch = 1000
 
 if($listToProcess -ne $null)
 {
 
     $numberOfItemsToRetrieve = $NumberOfItemsToDeleteInBatch;
     $camlQueryString = "<Where><Neq><FieldRef Name='ID'/><Value Type='Counter'>-1</Value></Neq></Where>"
     $viewFields="<FieldRef Name='ID' />"
     $camlQuery = New-Object -TypeName "Microsoft.SharePoint.SPQuery" -ArgumentList @($listToProcess.DefaultView);
     $camlQuery.Query=$camlQueryString;
     $camlQuery.ViewFields=$viewFields;
     $camlQuery.ViewFieldsOnly=$true
     $camlQuery.RowLimit=$numberOfItemsToRetrieve;
 
 
 do
 {
 
     $itemCount = 0;
     $listId = $listToProcess.ID;
     [System.Text.StringBuilder]$batchXml = New-Object "System.Text.StringBuilder";
     $batchXml.Append("<?xml version=`"1.0`" encoding=`"UTF-8`"?><Batch>");
     $command = [System.String]::Format( "<Method><SetList>{0}</SetList><SetVar Name=`"ID`">{1}</SetVar><SetVar Name=`"Cmd`">Delete</SetVar></Method>", $listId, "{0}" );
 
     $listItems = $listToProcess.GetItems($camlQuery)
     $camlQuery.ListItemCollectionPosition = $listItems.ListItemCollectionPosition
 
     foreach ($item in $listItems)
     {
         if($item -ne $null)
         {
             $batchXml.Append([System.String]::Format($command, $item.ID.ToString())) | Out-Null;$itemCount++;
         }
     }
     $batchXml.Append("</Batch>");
     $itemCount;
     $rootWeb.ProcessBatchData($batchXml.ToString())   | Out-Null;
 
     #Write-Host "Item deleted successfully" + $itemCount;
                     
 }
 
 while ($query.ListItemCollectionPosition -ne $null)
     $rootWeb.Dispose();
                 
 }
 else
 {
     #Write-Host "Cannot find list: " $listToProcess;
 }
 
 Stop-SPAssignment -Global -AssignmentCollection $assignmentCollection;
 
  #Write-Host "Finished";
 
 

One issue I found at the time of executing ps1 file script from C# & pipeline.invoke() was throwing error "Cannot invoke this function because the current host does not implement it." But it was deleted the list item successfully.

To resolve the issue I had to comment out Write-Host from ps1 script & execute the C# code, that’s it.

Let’s add one item in the list

clip_image002

As a result of executing the RunPsScript or the code written to execute batch file, the item will be deleted from the list

clip_image004

Happy Coding

Tarun Kumar Chatterjee

Categories