Null results after execution Powershell script in Visual Studio C# - c#

I'm trying to get some data from Azure Active Directory using C# code with reference to System.Management.Automation. I've got no errors with code execution, just null results and no output to textfile. Does anyone have this problem before or maybe I missed something? Thank you!
public void RunScriptTest()
{
string username = "Username";
string password = "Password";
List<String> listResults = new List<String>();
PowerShell powershell = PowerShell.Create();
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
powershell.Runspace = runspace;
powershell.AddScript("Install-Module -Name AzureAD -Force; \n");
powershell.AddScript("Import-Module -Name AzureAD -Verbose \n");
powershell.AddScript("$username = \"" + username + "\"; \n" +
"$password = convertTo-securestring '" + password + "' -AsPlainText -Force; \n" +
"$cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username, $password; \n" +
"Connect-AzureAD - Credential $cred; \n");
powershell.AddScript("Get-AzureADUser | Out-File -FilePath " + #"C:\TestResults\1.txt");
Collection<PSObject> results = powershell.Invoke();
runspace.Close();
StringBuilder stringBuilder = new StringBuilder();
foreach(PSObject obj in results)
{
listresults.Add(obj.ToString());
}
}

Sequencing .AddScript() calls without intervening .AddStatement() calls makes only the last .AddScript() call effective - all previous calls are ignored.
In order to examine errors that may have occurred during execution via .Invoke(), you must access the powershell.Streams.Error stream.
Therefore, the immediate fix is to replace your powershell.AddScript(...) calls with $powershell.AddStatement().AddScript(...)
Note that your PowerShellCode appears designed not to produce any output, so there's no point in trying to populate listresults.

Related

Uploading to Teams with C# and Powershell

I'm writing a form that will upload any file in a folder to a Microsoft Teams share. My code already manage to upload the files, but keep showing me the log in form of microsoft 365 for each file, instead asking only the first time.
this is the code:
private void Upload()
{
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
PowerShell psinstance = PowerShell.Create();
//fill some vars and connect (once, hopefully)
psinstance.AddScript("$SharepointURL = '" + LINK-TO-THE-DESIRED-TEAM + "'");
psinstance.AddScript("$OutPath = '" + SOURCE-DIRECTORY + "'");
psinstance.AddScript("Connect-PnPOnline -SPOManagementShell -url $SharepointURL -cleartokencache");
//upload each file in the directory
psinstance.AddScript("$Files = Get-ChildItem -path $OutPath");
psinstance.AddScript("foreach ($File in $Files){ Add-PnPFile -Folder '" + DESIRED-CHANNEL-DIRECTORY + "' -Path $File.FullName }");
//execute the script
Collection <PSObject> results = psinstance.Invoke();
//get possible errors
Collection<ErrorRecord> Errors = psinstance.Streams.Error.ReadAll();
runspace.Close();
}
The same powershell code, if executed in the powershell console, works like a sharm. This is driving me crazy, can you help me?

.Net core console application Stop-WebAppPool Remote connect via WinRM using Microsoft.PowerShell.SDK

I need help to solve problem to auto-mate deploy .Net Core web app Api to a server.
As you know is impossible to overwrite .dll if you not stop AppPool before , and there is no solution for that in IIS .
Actually by using PowerShell I can perform a script to do what I need the script is showed below:
PowerShell script
Actually I need a console application to performe same work , I found that I can use Microsoft.PowerShell.SDK to implement a solution.
public static void RunC()
{
string us = "xxxxxxxxxxxxx"; //User
string pw = "xxxxxxxxxxxxxxxx";//Passwprd
string sv = "xxx.x.xx.xxx";//Server
string apppoolname = "xxxxxxxxxxxxx";
StringBuilder script = new StringBuilder();
//Creazione script PS
script.Append("$password = ConvertTo-SecureString \"" + pw + "\" -AsPlainText -Force" + Environment.NewLine);
script.Append("$user = \"" + us + "\"" + Environment.NewLine);
script.Append("$cred = New-Object System.Management.Automation.PSCredential ($user,$password)" + Environment.NewLine);
script.Append("Enter-PSSession -ComputerName \"" + sv + "\" -Credential $cred" + Environment.NewLine);
script.Append("Import-Module webadministration");
script.Append("Stop-WebAppPool \"" + apppoolname + "\"");
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(script.ToString());
//pipeline.Commands.Add("Out-String");
Collection<PSObject> results = pipeline.Invoke();
StringBuilder sb = new StringBuilder();
foreach (PSObject pSObject in results)
{
sb.AppendLine(pSObject.ToString());
}
Console.WriteLine(sb.ToString());
}
But I got error show in the image , seams Module is not lodaded or something similar ..
some one can help me in some way?
Thank you :-)
I dont see the error in the image, but if it is a issue caused by the WebAdministration not being imported this should fix it:
#Requires -Modules WebAdministration
Place that at the begging of the script and it will try to import the module if it's not available already in the session. Microsoft documentation:
Specifies PowerShell modules that the script requires. Enter the module name and an optional version number.
If the required modules aren't in the current session, PowerShell imports them. If the modules can't be imported, PowerShell throws a terminating error.
source
Hope it helps, good luck.

Run PowerShell script in PowerShell 6 from C#

I have a PowerShell script which communicates with a REST server. This script only works in PowerShell 6.
I want to call it from C#, because the C# program needs the info from the REST server, and I don't want to rewrite the REST code in C#.
So basically, I want to run a PowerShell script from C#. However, in C#, PowerShell.Create(); creates a PowerShell instance that uses PowerShell 5.
I already replaced pwsh.exe in the default folder, deleted PowerShell 5 everywhere etc. and when I shift+right click anywhere to use "Run PowerShell here" I get a PowerShell 6 window. But for some reason, C# sticks to using PowerShell 5, when using the PowerShell class.
This is the PowerShell code I want to reuse:
function Get-JSONWebToken {
param (
[Parameter(Mandatory=$True)][string] $BaseUri,
[Parameter(Mandatory=$True)][string] $ApiToken
)
if ($PSVersionTable.PSVersion.Major -lt 6) {
$version = $PSVersionTable.PSVersion
Throw "Your PowerShell version is: $version. Please upgrade to PowerShell 6 or above"
}
$uri = "$BaseUri/auth/token"
$bodyJson = ConvertTo-Json #{token = $ApiToken} -Compress
Write-Host "Authenticating ..."
try {
$response = Invoke-RestMethod `
-Uri $uri `
-Method Post `
-ContentType "application/json" `
-Body $bodyJson
$jwtToken = $response.token
$secureToken = ConvertTo-SecureString $jwtToken -AsPlainText -Force
return $secureToken
}
catch {
#handle error
}
}
So now I am trying to call PowerShell 6 manually, importing a module first and then using it. Here are my three attempts, which are all supposed to do the same thing: call Get-JSONWebToken (in rest-api.psm1) and retrieve the output correctly.
C# version 1, using PowerShell class:
ps = PowerShell.Create();
//module import...
PSCommand cmd = ps.Commands.AddCommand("Get-JSONWebToken");
cmd.AddParameter("baseUri", baseUri);
cmd.AddParameter("apiToken", apiToken);
ps.Invoke();
This always runs on PowerShell 5 for some reason so it can't be used.
C# version 2, using a Process instead
Process ps6 = new Process();
ps6.StartInfo = new ProcessStartInfo {
FileName = "C:/Program Files/PowerShell/6/pwsh.exe",
Arguments = "-Command {\n" +
"Import-Module " + modulePath + ";\n" +
"Get-JSONWebToken " + apiToken + ";\n" +
"}",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = false
};
ps6.Start()
This runs on PowerShell 6, but only outputs the arguments I passed, and not the output of Get-JSONWebToken.
C# version 3: Calling PS6 from PS5 from C#
PSCommand cmd = ps.Commands.AddCommand("C:/Program Files/PowerShell/6/pwsh.exe");
ScriptBlock sb = ScriptBlock.Create("Import-Module " + modulePath + "; Get-JSONWebToken " + apiToken + ";");
cmd.AddParameter("Command", sb);
ps.Invoke();
This doesn't work at all:
Result: Usage: pwsh[.exe] [[-File] <filePath> [args]]
Result: [-Command { - | <script-block> [-args <arg-array>]
Result: | <string> [<CommandParameters>] } ]
Result: [-ConfigurationName <string>] [-CustomPipeName <string>]
...
...
PowerShell version:
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = $Ps6Path
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.CreateNoWindow = $false
$pinfo.Arguments = "-Command {Import-Module <myPath>\rest-api.psm1; Get-JSONWebToken 123inputStringExample;}"
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
$p.WaitForExit()
$stdout = $p.StandardOutput.ReadToEnd()
$stderr = $p.StandardError.ReadToEnd()
Write-Host "stdout: $stdout"
Write-Host "stderr: $stderr"
Write-Host "exit code: " + $p.ExitCode
This also only outputs the arguments I passed when called either from C# or from PS6 or PS5
This doesn't technically solve the problem, but I did as #MindSwipe suggested and rewrote the code in C# entirely. It wasn't trivially easy but it's a nice and elegant solution in the end.
If you have an idea on how to solve this question properly, please post it here as I'm still interested in how to call stuff on PowerShell 6 from C#.

Execute PowerShell cmdlet in C# script

I'm tying to use C# to connect to exchange and return the mailbox listing for a particular user. I am able to run this locally in PowerShell, however in the context of the script it's not showing what I expect.
I'm seeing outputs like tmp_1qf14mc3.wiv or tmp_ra1gbenl.ols instead of the mailbox listing.
Additional notes:
I have set up the reference to and imported the namespace of System.Management.Automation;
I'm running this through Visual Studio 2017, using the IIS Express build
This uses the .NET Core API project
.
using (PowerShell PowerShellInstance = PowerShell.Create())
{
// Attempt to get the mailbox listing for the user
PowerShellInstance.AddScript(
"$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList " + user + ",(ConvertTo-SecureString -String " + pass + " -AsPlainText -Force); " +
"$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $credential -Authentication Basic -AllowRedirection; " +
"Import-PSSession $Session -DisableNameChecking; " +
"Get-Mailbox; " +
"Remove-PSSession $Session");
// Invoke execution to collect the output
Collection<PSObject> PSOutput = PowerShellInstance.Invoke();
// Go through each output returned
foreach (PSObject outputItem in PSOutput)
{
// Dump the output to see what's coming out.
if (outputItem != null)
{
return new string[] { outputItem.ToString() };
}
}
}
return new string[] { "Did not get a powershell outputItem" };

Call PowerShell script file with parameters in C#

Really struggling with this. I have tried various different way, but nothing seems to work.
-using addScript: I get an error telling me that I can't call parameters this way an should use a UI like ISE ?!
-using FilePath parameter, I can't find the right way to pass the arguments (trouble binding)
This is the latest version I tried, and is lifting no errors, but the script is not executed, nothing happens...
Help would be much appreciated.
runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
pipeline = runspace.CreatePipeline();
string script =
#"{param($merchantName, $appType, $gruntDirectory, $merchantInstanceDirectory, $editorConnectionString) "+
_config.MerchantInstance.Directory + #"\Generate_And_Compile_LESS.ps1"
+ " –merchantName $merchantName"
+ " –appType $appType"
+ " –gruntDirectory $gruntDirectory"
+ " -merchantInstanceDirectory $merchantInstanceDirectory"
+ " -editorConnectionString $editorConnectionString }";
Command compileCommand = new Command("Invoke-Command");
compileCommand.Parameters.Add("Scriptblock", ScriptBlock.Create(script));
var args = new List<string>();
args.Add(merchantName);
args.Add(appType.GetHashCode().ToString());
args.Add("'" + _config.Grunt.Directory + "'");
args.Add("'" + _config.MerchantInstance.Directory + "'");
args.Add("'" + _connectionStrings.AppConnectionString + "'");
compileCommand.Parameters.Add("ArgumentList", String.Join(",", args));
pipeline.Commands.Add(compileCommand);
Collection<PSObject> results = pipeline.Invoke();
You can use this code, which I personally just tested.
static void Main(string[] args)
{
PowerShell ps = PowerShell.Create();
ps.AddScript(#"c:\test\test.ps1").AddParameter("param1", "paramvalue1");
ps.Invoke();
}
Here is my test script, located in c:\test\test.ps1.
[CmdletBinding()]
param (
[string] $param1
)
Set-Content -Path $PSScriptRoot\test.txt -Value $param1;
FYI, make sure that you launch 32-bit (x86) PowerShell, and set the execution policy to Unrestricted. Visual Studio is a 32-bit process, and invokes the 32-bit PowerShell engine by default.

Categories

Resources