Running Powershell from within SharePoint

Posted by Norgean on Geeks with Blogs See other posts from Geeks with Blogs or by Norgean
Published on Wed, 19 Sep 2012 11:17:20 GMT Indexed on 2012/09/21 15:39 UTC
Read the original article Hit count: 249

Filed under:

Just because something is a daft idea, doesn't mean it can't be done.

We sometimes need to do some housekeeping - like delete old files or list items or… yes, well, whatever you use Powershell for in a SharePoint world. Or it could be that your solution has "issues" for which you have Powershell solutions, but not the budget to transform into proper bug fixes. So you create a "how to" for the ITPro guys.

Idea: What if we keep the scripts in a list, and have SharePoint execute the scripts on demand? An announcements list (because of the multiline body field).

Warning! Let us be clear. This list needs to be locked down; if somebody creates a malicious script and you run it, I cannot help you.

First; we need to figure out how to start Powershell scripts from C#. Hit teh interwebs and the Googlie, and you may find jpmik's post: http://www.codeproject.com/Articles/18229/How-to-run-PowerShell-scripts-from-C. (Or MS' official answer at http://msdn.microsoft.com/en-us/library/ee706563(v=vs.85).aspx)

        public string RunPowershell(string powershellText, SPWeb web, string param1, string param2)        {
            // Powershell  ~= RunspaceFactory - i.e. Create a powershell context
            var runspace = RunspaceFactory.CreateRunspace();

            var resultString = new StringBuilder();
            try
            {
	// load the SharePoint snapin - Note: you cannot do this in the script itself (i.e. add-pssnapin etc does not work)
	     PSSnapInException snapInError;
                runspace.RunspaceConfiguration.AddPSSnapIn("Microsoft.SharePoint.PowerShell", out snapInError);

                runspace.Open();

                // set a web variable.
                runspace.SessionStateProxy.SetVariable("webContext", web);
// and some user defined parameters
                runspace.SessionStateProxy.SetVariable("param1", param1);
                runspace.SessionStateProxy.SetVariable("param2", param2);

                var pipeline = runspace.CreatePipeline();
                pipeline.Commands.AddScript(powershellText);

		// add a "return" variable
                pipeline.Commands.Add("Out-String");

                // execute!
                var results = pipeline.Invoke();

                // convert the script result into a single string
                foreach (PSObject obj in results)
                {
                    resultString.AppendLine(obj.ToString());
                }
            }
            finally 
            {
                // close the runspace
                runspace.Close();
            }
            // consider logging the result. Or something.
            return resultString.ToString();
        }

Ok. We've written some code. Let us test it.

            var runner = new PowershellRunner();
            runner.RunPowershellScript(@"
                        $web = Get-SPWeb 'http://server/web' # or $webContext
                        $web.Title = $param1
                        $web.Update()
                        $web.Dispose()
                    ", null, "New title", "not used");

Next step: Connect the code to the list, or more specifically, have the code execute on one (or several) list items. As there are more options than readers, I'll leave this as an exercise for the reader. Some alternatives:

  • Create a ribbon button that calls RunPowershell with the body of the selected items
  • Add a layout page
    • Specify list item from query string (possibly coupled with content editor webpart with html that links directly to this page with querystring)
  • Webpart
  • Listing with an "execute" column
  • List with multiselect and an execute button

Etc!


Now that you have the code for executing powershell scripts, you can easily expand this into a timer job, which executes scripts at regular intervals. But if the previous solution was dangerous, this is even worse - the scripts will usually be run with one of the admin accounts, and can do pretty much anything...


One more thing... Note that as this is running "consoleless" calls to Write-Host will fail. Two solutions; remove all output, or check if the script is run in a console-window or not.

 

if ($host.Name -eq "ConsoleHost")
{
	Write-Host 'If I agreed with you we'd both be wrong'
}

© Geeks with Blogs or respective owner