Is there any way to clone Powershell Runspace object - powershell

I want to know if it is possible to create a new Powershell Runspace object from an old one.
I'm am going to do some powershell operations concurrently. I create powershell runspace every time and run certain commands. Let's say the first 5 commands are same for all operations. If I could run those commands only once for all operations and send a copy of the runspace to the multi threading method, it would be more efficient.
Means, I run some commands through a pipeline first.
Runspace runspace = RunspaceFactory.CreateRunspace()
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript("$var = 5"); //those 5 initial commands
pipeline.Invoke();
Now, I want to run certain commands concurrently.
Parallel.For(0, 5, new ParallelOptions { }, i => concurrentOperations(runspace, i));
The concurrentOperations method has been defined as this
private static void concurrentOperations(Runspace runspace, int i)
{
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript("$newVar = $var + " + i + "; $newVar"); //newer commands which differs for each operation
runspace.Open();
System.Collections.ObjectModel.Collection<PSObject> result = pipeline.Invoke();
foreach (PSObject obj in result)
{
Console.WriteLine("obj" + i + " : " + obj);
}
}
And now I encounter an exception that, "Pipelines cannot be run concurrently".
So, If I could make a copy of the runspace, the pipelines will be created for different runspaces only. But runspace doesnot have Clone() method in it.
Is there any way that I could achive this?

Related

Using Jenkins defined variables in PowerShell block

For example, there is a Jenkins stage with defined variable - jenkinsVariable = 5
How can I access that variable in the powershell block? Is that possible like this or I need to call powershell.ps1 with parameters in order to transfer variable from jenkins to powershell area?
stage (testStage)
{
def jenkinsVariable = 5;
def result = powershell (returnStdout: true, script: 'Write-Output jenkinsVariable')
}

Assign value to powershell variable within Jenkins pipeline

I came across several documents on how to run a Powershell script within Jenkins pipeline and how to capture output.
However I want to use the captured output for next node powershell script.
e.g.
node {
def msg = powershell(returnStdout: true, script: 'Write-Output "PowerShell is mighty!"')
}
Now I want to use msg in the next node within Powershell script. Like if we can assign it to a powershell variable and then perform operations with that variable.
Any pointers on how this can be achieved?
You can assign the powershell output to an environment variable and use that in the subsequent nodes:
node {
env.msg = powershell(returnStdout: true, script: 'Write-Output "PowerShell is mighty!"')
}
node {
def output = powershell(returnStdout: true, script: '''
$message = ($env:msg).trim()
Write-Output $message
''')
println(output)
}
Notice the env prefix before variable msg. You can retrieve the variable within powershell in next node using $env: prefix followed by variable name. Don't forget to trim the variable (.trim()) within powershell to remove newline.

Run powershell commands atomically

I have 3 powershell commands that need to run synchronously, as I need to pass the output from the 1st to the 2nd, and so on. However if any fail I need to back out of all 3. Is there a cmdlet or technique, even, that will enable this behavior in powershell?
You could wrap each of your commands in an object or a hashtable with a corresponding "rollback" action and a test:
$Commands = #(
#{
Action = { Set-Value $PathOne "newValue" }
RollBack = { Set-Value $PathOne "oldValue" }
Test = { Test-Something }
}
#{
Action = { Set-Value $PathTwo "newValue" }
RollBack = { Set-Value $PathTwo "oldValue" }
Test = { Test-Something }
}
)
Then use a Stack<Scriptblock> to keep track of actions that need to be executed in the event of a "roll-back"
$RollbackStack = [System.Collections.Generic.Stack[Scriptblock]]::new()
foreach($Command in $Commands){
# Execute command
& $Command.Action
# Add rollback action to stack
$RollbackStack.Push($Command.Rollback)
# Test result, roll back if failed
if(-not(& $Command.Test)){
foreach($rollbackAction in $RollbackStack){
& $rollbackAction
}
}
}
In general, PowerShell commands do not support "backing out" or "rollback". The Registry provider does support transactions (which would allow this kind of behavior), but only with operations involving the registry.
It might be possible to capture the state of things that you're updating in the commands and re-applying those states, but it would be a very manual process.

Set Execution Policy Powershell in Azure web application

I need to execute some powershell code from a web api which I have deployed in an Azure App Service. I couldn't achieve to se 'Set-ExecutionPolicy' because I need to set it to unrestricted but I get an error
File D:\home\powershell\teams_v2.psm1 cannot be loaded because running scripts is disabled on this system. For more information, see about_Execution_Policies at http://go.microsoft.com/fwlink/?LinkID=135170
.
I have the following code
using (Runspace runspace = RunspaceFactory.CreateRunspace())
{
runspace.Open();
var script = String.Format(#"Import-Module 'D:\home\powershell\teams_v2.psm1'
connect-teamsservice -user admin#contoso.onmicrosoft.com -tenant contoso.onmicrosoft.com
new-Team -displayname '{0}' -description '{1}' -smtpaddress '{2}' -alias '{3}' -type 'private'",
group.Name, group.Description, String.Format("{0}#contoso.onmicrosoft.com", group.MailNickName), "team");
RunspaceInvoke scriptInvoker = new RunspaceInvoke();
// set powershell execution policy to unrestricted
//scriptInvoker.Invoke("Set-ExecutionPolicy Unrestricted");
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(script);
// add an extra command to transform the script
// output objects into nicely formatted strings
// remove this line to get the actual objects
// that the script returns. For example, the script
// "Get-Process" returns a collection
// of System.Diagnostics.Process instances.
pipeline.Commands.Add("Out-String");
// execute the script
Collection <PSObject> results = pipeline.Invoke();
// close the runspace
runspace.Close();
// convert the script result into a single string
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
}
How can I achieve this to correctly load the ps module and use its functions.

Pass ProcessParameters runtime (MSBuildArguments) using Powershell to tfs 2010 build definitions

I am executing builds using powershell script. I need to pass the process parameters run time based on the commandline arguments passed to the script. I am using TFS 2010.
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Client")
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Build.Client")
$projectName = "test"
$buildName = "test.Build"
$tfsServer="xxx"
$tfsInstance = [Microsoft.TeamFoundation.Client.TeamFoundationServerFactory]::GetServer($tfsServer)
$buildService = $tfs.GetService([Microsoft.TeamFoundation.Build.Client.IBuildServer])
$buildDefinations = $buildService.QueryBuildDefinitions($projName)
Loop through all the builds to find the one we are looking for
foreach ($build in $buildDefinations)
{
Get the name of this build instance
$bNameInstance = $build.Name
$ClientName = "test1" #default set in the builddefination is "/p:xxx=test"
#Get the process parameters. I need to update the default value. How can we process the dictionary and update the value
$bMSBuildArguments = $build.ProcessParameters
#Once setting is done."/p:xxx=test1"
$build.ProcessParameters = $bMSBuildArguments
[Void]$buildService.QueueBuild($build)
}
I need help in updating the processparameters using the powershell code. I came across the C# (http://blogs.msdn.com/b/jpricket/archive/2010/03/25/tfs2010-queuing-a-build-from-code-with-custom-process-parameter-values.aspx)solution but not able convert that to Powershell
The answer is in the blog post provided. Try something like this:
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Build.Workflow")
$request = $build.CreateBuildRequest()
$process = [Microsoft.TeamFoundation.Build.Workflow.WorkflowHelpers]::DeserializeProcessParameters($build.ProcessParameters)
#make changes to your process parameters in $process
$process.Item("asdf") = "new value"
$request.ProcessParameters = [Microsoft.TeamFoundation.Build.Workflow.WorkflowHelpers]::SerializeProcessParameters($process)
$buildService.QueueBuild($request)