I am trying to setup a simple .aspx web page that will accept a user's input of a string (later, more than one string) and use that string as the parameter value for a Powershell script.
The PS script looks like this right now:
[CmdletBinding()]
param (
[string] $ServiceName
)
$ServiceName | out-file c:\it\test.txt
$ServiceName | Out-String
The C# code looks like this:
var shell = PowerShell.Create();
// Add the script to the PowerShell object
shell.Commands.AddScript("C:\\it\\test.ps1 -ServiceName BITS");
// Execute the script
var results = shell.Invoke();
When I run that, I get "BITS" written to the test.txt file. What I need to do now, is setup the application to call the script, passing in the "ServiceName" parameter. I found this: Call PowerShell script file with parameters in C# and tried the following code:
PowerShell ps = PowerShell.Create();
ps.AddScript(#"c:\it\test.ps1").AddParameter("ServiceName", "BITS");
var results = ps.Invoke();
In this case, the script was called and the test.txt file was created, but the value (BITS) was not written to the file. What am I missing here? Why isn't the parameter being passed to the script?
Thanks.
I ended up using
var ps = #"C:\it\test.ps1";
processInfo = new ProcessStartInfo("powershell.exe", "-File " + ps + " -ServiceName BITS);
I don't like this as much, but it works. shrug
Here are three possible solutions for future readers including me:
using (PowerShell ps = PowerShell.Create())
{
//Solution #1
//ps.AddCommand(#"C:\it\test.ps1", true).AddParameter("ServiceName", "BITS");
//Solution #2
//ps.AddScript(#"C:\it\test.ps1 -ServiceName 'BITS'", true);
//Solution #3
ps.AddScript(File.ReadAllText(#"C:\it\test.ps1"), true).AddParameter("ServiceName", "BITS");
Collection<PSObject> results = ps.Invoke();
}
I haven't seen solution #3 documented anywhere else, though I got the idea for it from https://blogs.msdn.microsoft.com/kebab/2014/04/28/executing-powershell-scripts-from-c/
Related
I need to get which network interface is connected to which network. I found that this information is accessible in MSFT_NetConnectionProfile. Unfortunatelly I cannot access it directly from C# (I get ManagementException: Provider load failure on computer where it should run) but when I access it from PowerShell, it works. Then my idea is to run PowerShell command from C# but I cannot get the result.
using System.Management.Automation;
string command = "Get-WmiObject -Namespace root/StandardCimv2 -Class MSFT_NetConnectionProfile | Select-Object -Property InterfaceAlias, Name";
PowerShell psinstance = PowerShell.Create();
psinstance.Commands.AddScript(command);
var results = psinstance.Invoke();
foreach (var psObject in results)
{
/* Get name and interfaceAlias */
}
The code runs without errors but results are empty. I tried even adding Out-File -FilePath <path-to-file> with relative and absolute file path but no file was created. I even tried old >> <path-to-file> but without luck. When I added Out-String then there was one result but it was empty string.
When I tested the commands directly in PowerShell then it worked. Is there a way how to get it in C#?
The PS commands must be constructed in a builder-pattern fashion.
Additionally, in PS Core the Get-WmiObject has been replaced by the Get-CimInstance CmdLet.
The following snippet is working on my env:
var result = PowerShell.Create()
.AddCommand("Get-CimInstance")
.AddParameter("Namespace", "root/StandardCimv2")
.AddParameter("Class", "MSFT_NetConnectionProfile")
.Invoke();
Is there way to convert invoked powershell command from C# to string?.
Let's say for example i have something like this:
PowerShell ps = PowerShell.Create();
ps.AddCommand("Add-VpnConnection");
ps.AddParameter("Name", "VPN_" + ClientName);
ps.AddParameter("ServerAddress", VPN_SERVER_IP);
ps.AddParameter("AllUserConnection");
ps.AddParameter("SplitTunneling", true);
ps.AddParameter("TunnelType", "L2tp");
And i would like to save invoked command to log file.
Can i somehow return whole command as string?
I believe what you want essentially is this.
PowerShell ps = PowerShell.Create();
ps.AddScript($"Add-VpnConnection -Name \"VPN_{ClientName}\" -ServerAddress {VPNServerIP} -AllUserConnection -SplitTunneling -TunnelType L2tp");
ps.Invoke();
The invoke return will contain a collection of PSObject so you can read it and save the information like you want in a log in c#.
Note: This answer does not solve the OP's problem. Instead, it shows how to capture a PowerShell command's output as a string in C#, formatted in the same way that the command's output would print to the display (console), if it were run in an interactive PowerShell session.
Out-String is the cmdlet that produces formatted, for-display representations of output objects as strings, as they would print to the screen in a PowerShell console.
Therefore, you simply need to use another .AddCommand() in order to pipe the output from your Add-VpnConnection call to Out-String:
string formattedOutput;
using (PowerShell ps = PowerShell.Create())
{
ps.AddCommand("Add-VpnConnection")
.AddParameter("Name", "VPN_" + ClientName)
.AddParameter("ServerAddress")
.AddParameter("AllUserConnection", VPN_SERVER_IP)
.AddParameter("SplitTunneling", true)
.AddParameter("TunnelType", "L2tp");
// Add an Out-String call to which the previous command's output is piped to.
// Use a -Width argument (column count) large enough to show all data.
ps.AddCommand("Out-String").AddParameter("Width", 512);
// Due to use of Out-String, a *single string* is effectively returned,
// as the only element of the output collection.
formattedOutput = ps.Invoke<string>()[0];
}
Console.Write(formattedOutput);
I have instantiated a C# PowerShell object and added scripts like so:
var ps = PowerShell.Create();
ps.AddScript(#"& ""filename.ps1"" ""machine5"" ""stop"" ");
Collection<PSObject> results = ps.Invoke();
For reference, here are the contents of filename.ps1:
param([string] $middleTier, [string] $mode)
Write-Verbose "This does appear"
if($mode -eq "stop") {
Invoke-Command -ComputerName $middleTier -ScriptBlock {
Write-Verbose "This does not appear"
Stop-Service -Name "ARealService" -Force -Verbose
}
iisreset $middleTier /stop
}
However, after a call to ps.Invoke(), all of my ps.Streams collections are empty. I know for a fact that this script writes to the verbose stream when run, but the C# Powershell object does not seem to capture any of it. What am I doing wrong?
Edit: I have added some explicit calls to Write-Vebose to show what does and does not get captured. It is clear that there is a problem with getting the stream output from the Invoke-Command block.
Your script block has its own verbose preference ($VerbosePreference). Set it to Continue inside script block then it should work.
I'm creating a Windows Service that calls a Powershell script every minute. The Powershell script returns local system information.
function MachineInformation
{
[hashtable]$machine = #{}
$computerSystem = get-wmiobject Win32_ComputerSystem
$machine.machine = $computerSystem.Name
$machine.key = $computerSystem.Manufacturer
[String]$machine.value = Get-WmiObject win32_processor | Measure-Object -property LoadPercentage -Average | Select Average
[DateTime]$machine.timestamp = Get-Date
Return $machine
}
MachineInformation
When I run in Powershell ISE it works.
My C# Windows Service then tries to invoke the script
PowerShell ps = PowerShell.Create();
ps.AddScript("C:\\Scripts\\SystemInfo.ps1");
Collection<PSObject> results = ps.Invoke();
foreach (PSObject result in results)
{
//Do something
}
When debugging, results is returning a count of 0. This was working fine a few days ago and now it has decided to stop. It has been driving me crazy for hours. What am i doing wrong?
Change the build type from 32 bit to 64 bit, this should solve your problem.
Actually I did a project and built in "Any CPU" mode and it works fine. My problema was about "Location". I figured out this trying execute the script from another folder, the problem is that in C# I couldnĀ“t see the errors messages.
To solve my problem I inserted the "Set-Location" cmdlet before call my script, eg.: "Set-Location 'c:\myscriptlocation'; c:\myscriptlocation\myscript.ps1".
So a while back I wrote a Powershell script that would Parse an IIS Log and then do some stuff (email, create a report, and other nifty stuff). Well I am working on excuting that from a C# console app. Before I post my code I just wanted to make one thing clear, i would like to try and stay away from log parser to parse this log cause of many reasons but one specifically, why use something when you can write something else to your liking ;). So here is my code
PS Script:
$t1 =(get-date).AddMinutes(-10)
$t2 =$t1.ToUniversalTime().ToString("HH:mm:ss")
$IISLogPath = "C:\inetpub\logs\LogFiles\W3SVC1\"+"u_ex"+(get-date).AddDays(-3).ToString("yyMMdd")+".log"
$IISLogFileRaw = [System.IO.File]::ReadAllLines($IISLogPath)
$headers = $IISLogFileRaw[3].split(" ")
$headers = $headers | where {$_ -ne "#Fields:"}
$IISLogFileCSV = Import-Csv -Delimiter " " -Header $headers -Path $IISLogPath
$IISLogFileCSV = $IISLogFileCSV | where {$_.date -notlike "#*"}
$tape = $IISLogFileCSV | Format-Table time,s-ip,cs-uri-stem | Out-Host
C# app thus far:
Runspace runSpace = RunspaceFactory.CreateRunspace();
runSpace.Open();
Pipeline pipeline = runSpace.CreatePipeline();
pipeline.Commands.AddScript(#"D:\PS-Scripts\IIS\IISLogScan.ps1");
Collection<PSObject> results = pipeline.Invoke();
foreach (PSObject obj in results)
{
Console.WriteLine(results);
}
Console.ReadLine();
Now when I run my App it just sits and doesnt display anything and i Set my breakpoints and it says that there is nothing to display within my foreach which i am completely hung up on cause running my ps1 script it works perfectly and shows many lines. Any insight anyone can give would be great.
Try changing in .ps1 file:
$global:tape =$IISLogFileCSV | Format-Table time,s-ip,cs-uri-stem
and in c#
pipeline.Commands.AddScript(#"D:\PS-Scripts\IIS\IISLogScan.ps1");
pipeline.Commands.AddScript("$tape");
pipeline.Commands.AddScript("out-string");
or in .ps1:
$tape =$IISLogFileCSV | Format-Table time,s-ip,cs-uri-stem
$tape
and in c#
pipeline.Commands.AddScript(#"D:\PS-Scripts\IIS\IISLogScan.ps1");
pipeline.Commands.AddScript("out-string");