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");
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();
The image shows the same powershell command executed in a C# and Powershell ISE. My question is, Powershell shows complete list of process and process directory, but the result of the code executed in C# does not contain windows processes like svchost.exe.
Of course, both my C# Form and Powershell ISE are running as Administrator.
Also, I made sure to code my form to launch with administrative privileges.
To be double sure, I executed my C# by right click - Run as Administrator (what I also did with Powershell ISE)
Command/s: Powershell and C#
get-process | get-item -erroraction silentlycontinue | format-table name, directory
get-process | format-table name, directory
Although this particular command shows all the process:
get-process
Unfortunately, combining it with the syntax above will show different output from C#.
private string RunScript(string script)
{
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(script);
pipeline.Commands.Add("Out-String");
Collection<PSObject> results = pipeline.Invoke();
runspace.Close();
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject pSObject in results)
stringBuilder.AppendLine(pSObject.ToString());
return stringBuilder.ToString();
}
private void button1_Click(object sender, EventArgs e)
{
output.Clear();
output.Text = RunFilePs1(input.Text);
}
For some reason, Get-Process doesn't return the Path of some processes (probably the 64-bit ones) when running in 32-bit mode, so passing those to Get-Item fails to find anything.
You can see this by running your PS script from the x86 PS ISE.
So if you switch your C# Form to 64-bit, it will return all processes.
I am using a script to update the data of Active Directory users
Import-Csv -Path $Path | foreach-object {Set-ADUser -Identity ($_.user) -Department $_.Department -Title $_.Title -Company $_.Company -MobilePhone $_.MobilePhone ...etc (the required data to modify)}
I want the script to write the errors in the update of an "x" user's, to see whose user wasn't updated.
I'm creating a C# app to write a personalized script, and still need to make the error log.
The idea is to create this log in a specified path. I don't know if this can be made in the script it self.
I'm using this PowerShell method I have found here:
PowerShell ps = PowerShell.Create();
ps.AddScript(script);
IAsyncResult result = ps.BeginInvoke();
// do something else until execution has completed.
// this could be sleep/wait, or perhaps some other work
while (result.IsCompleted == false)
{
//think i can make that here, but canĀ“t see how
}
MessageBox.Show("Complete");
Perhaps something like this can be used to get you going quickly. Without seeing the script, i'll have to simply assume that you have a ps1 file, and you want the errors it generates:
.\myhugescript.ps1 | Out-File -Path D:\log.log -Append
$(.\myhugescript.ps1) | Out-File -Path D:\log.log
You can write errors to a file like this:
$logFilePath = "..."
Import-Csv -Path $Path | foreach-object {
Try {
$user = $_.user
Set-ADUser -Identity $user -Department $_.Department -Title $_.Title -Company $_.Company -MobilePhone $_.MobilePhone ...etc (the required data to modify)
} Catch [system.exception] {
$t = get-date -format "yyyy-MM-dd HH:mm:ss"
$msg = "$t -- Error updating user $user: $($_.Exception.Message)`n"
$msg | Out-File -Append $logFilePath
}
}
You could also replace Out-File with Write-Error to write to the stderr stream and then read that stream directly in the C# code, to save the disk I/O. There's an example of what that looks like here:
Get Powershell command's output when invoked through code
Replace the Progress stream in that example with the Error stream.
I need to run this command from C#
Get-AppxPackage -AllUsers | Foreach {Add-AppxPackage -DisableDevelopmentMode -Register "$($_.InstallLocation)\AppXManifest.xml"}
and i use this code:
Runspace runspace2 = RunspaceFactory.CreateRunspace();
runspace2.Open();
Pipeline pipeline2 = runspace2.CreatePipeline();
pipeline2.Commands.AddScript("Get-AppxPackage -AllUsers | Foreach {Add-AppxPackage -DisableDevelopmentMode -Register \"$($_.InstallLocation)\\AppXManifest.xml\"}");
pipeline2.Invoke();
runspace2.Close();
The code return error:
C:\AppXManifest.xml not Found
But if i run this command from powershell it work and install all Apps so i think is an error for special characters in this part "$($_.InstallLocation)\AppXManifest.xml", suggestions?
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/